SlideShare a Scribd company logo
Creating a modern web
application using Symfony API
Platform, ReactJS and Redux
by Jesus Manuel Olivas / weKnow
@jmolivas
● WHO AM I?
Jesus Manuel Olivas
jmolivas@weknowinc.com
jmolivas
jmolivas
drupal.org/u/jmolivas
jmolivas.weknowinc.com
WeGive
2,572,697
WeAre
WeKnow
Symfony
API Platform / GraphQL
 ReactJS / Redux / Saga
Ant Design
Symfony Flex
Symfony Flex … a Composer plugin for Symfony
> Symfony Flex is a Composer plugin that modifies the behavior of
the require, update, and remove composer commands.
> Symfony Flex automates the most common tasks of Symfony
applications, like installing and removing bundles and other
dependencies using recipes defined in a manifest.json file.
Directory structure
API Platform 
Framework
The API Platform Framework
REST and GraphQL
framework to build
modern API-driven
projects
https://api-platform.com/
Built on the Shoulders of Giants
> Extend the framework with thousands of existing Symfony
bundles and React components.
> The API component includes the Symfony 4 flex, the Doctrine ORM.
Client-side components and Admin based on React and a Docker
configuration ready to startup your project using one single command.
> Reuse all your Symfony, React and Docker skills and benefit of their high
quality docs; you are in known territory.
The API Platform Components
API Schema Admin CRUD
Try API-Platform
# Clone code repository

git clone https://github.com/api-platform/api-platform.git
Recommendations and adjustments
> Update route prefix at api/config/routes/api_platform.yaml
file.
api_platform:

…

prefix: /api
> Update admin/.env and client/.env files (change protocol and port).
REACT_APP_API_ENTRYPOINT=http://localhost:8080/api
Start containers … and grab water, coffee, or a beer.
# Start containers
ahoy up
# Open browser
open http://localhost/
Add more formats
Update api/config/packages/api_platform.yaml adding:
formats:
jsonld: [‘application/ld+json'] # first one is the default format
json: ['application/json']
jsonhal: ['application/hal+json']
xml: ['application/xml', 'text/xml']
yaml: ['application/x-yaml']
csv: ['text/csv']
html: ['text/html']
Add Dependencies
> Add new entities to api/src/Entity/ directory:

ahoy console make:entity



> Remove default entity

api/src/Entity/Greeting.php
Creating a modern web application using Symfony API Platform, ReactJS and Redux DrupalCampLA
Creating a modern web application using Symfony API Platform, ReactJS and Redux DrupalCampLA
Creating a modern web application using Symfony API Platform, ReactJS and Redux DrupalCampLA
Creating a modern web application using Symfony API Platform, ReactJS and Redux DrupalCampLA
api/src/Entity/Post.php 1/3
<?php

namespace AppEntity;



use ApiPlatformCoreAnnotationApiResource;

use DoctrineORMMapping as ORM;

use SymfonyComponentValidatorConstraints as Assert;
/**

* @ApiResource

* @ORMTable(name="post")

* @ORMEntity

*/
class Post
{
…
}
api/src/Entity/Post.php 2/3
/**

* @ORMId

* @ORMGeneratedValue(strategy="AUTO")

* @ORMColumn(type="integer")

*/

private $id;
/**

* @ORMColumn

* @AssertNotBlank

*/

public $title = '';

/**

* @ORMColumn
* @AssertNotBlank
*/
public $body = '';
api/src/Entity/Post.php 3/3
/**
* @ORMManyToOne(targetEntity="PostType")
* @ORMJoinColumn(name="post_type_id", referencedColumnName="id", nullable=false)
*/
public $type;
public function getId(): int
{
return $this->id;
}
Tracking Database changes
# Add dependency
ahoy composer require migrations
# Execute command(s)
ahoy console make:migration
ahoy console doctrine:migrations:migrate
Add FOSUserBundle
# Add dependency
composer require friendsofsymfony/user-bundle
composer require symfony/swiftmailer-bundle
https://symfony.com/doc/current/bundles/FOSUserBundle/index.html
https://jolicode.com/blog/do-not-use-fosuserbundle
Initialize the project
> Drop and Create Database
> Execute Migrations
> Populate Entities with Data
bin/console init
Use:

hautelook/alice-bundle 

doctrine/data-fixtures
Loading Posts using the Browser
http://localhost:8080/api/posts
http://localhost:8080/api/posts.json
http://localhost:8080/api/posts.jsonld
http://localhost:8080/api/posts/1
http://localhost:8080/api/posts/1.json
Loading Posts using the CLI
curl -X GET "http://localhost:8080/api/posts" 
-H "accept: application/json"
curl -X GET "http://localhost:8080/api/posts/1" 
-H "accept: application/ld+json"
ADD Posts from CLI
curl -X POST "http://localhost:8080/api/posts" 
-H "accept: application/ld+json" 
-H "Content-Type: application/ld+json" 
-d '{ "title": "Post create from CLI", "body": "body-
less", "type": "/api/post_types/1"}'
UPDATE and REMOVE Posts from CLI
curl -X PUT "http://localhost:8080/api/posts/9" 
-H "accept: application/ld+json" 
-H "Content-Type: application/ld+json" 
-d '{ "title": "Updated from CLI"}'
curl -X DELETE "http://localhost:8080/api/posts/10" 
-H "accept: application/json"
Serialization
> API Platform allows to specify the which attributes of the resource are
exposed during the normalization (read) and denormalization (write)
process. It relies on the serialization (and deserialization) groups feature of
the Symfony Serializer component.
> In addition to groups, you can use any option supported by the Symfony
Serializer such as enable_max_depth to limit the serialization depth.
Serialization Relations (Post => PostType) 1/2
# api/src/Entity/Post.php & PostType.php
* @ApiResource(attributes={
* "normalization_context"={"groups"={"read"}},
* "denormalization_context"={"groups"={"write"}}
* })
Serialization Relations (Post => PostType) 2/2
# Add use keyword to class
use SymfonyComponentSerializerAnnotationGroups;
# Add use keyword to properties
* @Groups({"read"})
* @Groups({"read", "write"})
GraphQL
GraphQL
GraphQL offers significantly more flexibility for integrators.
Allows you to define in detail the only the data you want.
GraphQL lets you replace multiple REST requests with a single
call to fetch the data you specify.
Add GraphQL
To enable GraphQL and GraphiQL interface in your API, simply require
the graphql-php package using Composer:
composer require webonyx/graphql-php
open http://localhost:8080/api/graphql
Disable GraphiQL
Update api/config/packages/api_platform.yaml adding:
api_platform:
# ...
graphql:
graphiql:
enabled: false
# ...
Load resource using GraphQL
{
post (id:"/api/posts/1") {
id,
title,
body
}
}
Load resource using GraphQL form the CLI
curl -X POST 
-H "Content-Type: application/json" 
-d '{ "query": "{ post(id:"/api/posts/1") { id,
title, body }}" }' 
http://localhost:8080/api/graphql
Load resource relations using GraphQL
{
post (id:"/api/posts/1") {
title,
body,
type {
id,
name,
}
}
}
Load resource relations using GraphQL form the CLI
curl -X POST 
-H "Content-Type: application/json" 
-d '{ "query": "{ post(id:"/api/posts/1") { id, title,
body, type { id, name } }}" }' 
http://localhost:8080/api/graphql
JWT
JWT Dependencies
# JWT
composer require lexik/jwt-authentication-bundle
JWT Refresh

gesdinet/jwt-refresh-token-bundle
JWT Events (create)
# config/services.yaml

AppEventListenerJWTCreatedListener:

tags:

- {

name: kernel.event_listener, 

event: lexik_jwt_authentication.on_jwt_created,

method: onJWTCreated

}


# src/EventListener/JWTCreatedListener.php

public function onJWTCreated(JWTCreatedEvent $event)

{

$data = $event->getData();

$user = $event->getUser();

$data['organization'] = $user->getOrganization()->getId();

$event->setData($data);

}
JWT Events (success)
# config/services.yaml

AppEventListenerAuthenticationSuccessListener:

tags:

- {

name: kernel.event_listener, 

event: lexik_jwt_authentication.on_authentication_success,

method: onAuthenticationSuccessResponse

}

# src/EventListener/AuthenticationSuccessListener.php

public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event) 

{

$data = $event->getData();

$user = $event->getUser();

$data[‘roles'] = $user->getOrganization()->getRoles();

$event->setData($data);

}
React+Redux+Saga+
AntDesign
dvajs/dva - React and redux based framework. 
https://github.com/dvajs/dva
React / Redux / Saga / AntDesig
> Use webpack instead of roadhog.
> Use apollo-fetch for GraphQL calls.
> Use jwt-decode to interact with JWT.
> Use LocalStorage for simple key/values storage.
> Use IndexedDB for encrypted and/or more complex data structures.
> Use Socket-IO + Redis to sync API with ReactJS client.
Tips
Directory Structure
├── package.json
├── src
│   ├── components
│   ├── constants.js
│   ├── index.js
│   ├── models
│   ├── router.js
│   ├── routes
│   └── services
└── webpack.config.js
constants.js
export const API_URL = process.env.API_ENTRY_POINT;
export const GRAPHQL_URL = `${API_URL}/api/graphql`;
export const NOTIFICATION_URL = process.env.NOTIFICATION_URL;
export const NOTIFICATION_ICON = {
info: 'info',
warning: 'exclamation',
error: 'close',
success: 'check'
};
index.js
import dva from 'dva';

import auth from './models/auth';

import local from './models/local';

import ui from './models/ui';
const app = dva({

…

});



# Register global models

app.model(auth);

app.model(local);

app.model(ui);

app.router(require('./router'));

app.start(‘#root');
router.js 1/2
import …

import AuthRoute from './components/Auth/AuthRoute';
export default function RouterConfig({ history, app }) {

const Login = dynamic({

app,

component: () => import('./routes/Login'),

});
const routes = [{

path: '/posts',

models: () => [

import('./models/posts')

],

component: () => import('./routes/Posts'),

}, {
path: '/posts/:id',

models: () => [

import('./models/projects'),

],

component: () => import('./routes/Posts'),

}];
router.js 2/2
return (
<Router history={history}>

<Switch>

<Route exact path="/" render={() => (<Redirect to="/login" />)} />

<Route exact path="/login" component={Login} />

{

routes.map(({ path, onEnter, ...dynamics }, key) => (
<AuthRoute

key={key} exact path={path}

component={dynamic({

app,

...dynamics,

})}

/>

))
}

</Switch>

</Router>

);

}
src/components/Auth/AuthRoute.js
import {Route, Redirect} from "dva/router";

…
class AuthRoute extends Route {
async isTokenValid() {

# Check for token and refresh if not valid 

}
render() {
return (
<Async
promise={this.isTokenValid()}
then={(isValid) => isValid ?
<Route path={this.props.path} component={this.props.component} />
:
<Redirect to={{ pathname: '/login' }}/>
}
/>
);
};
src/models/posts.js 1/3
import {Route, Redirect} from "dva/router";

import * as postService from '../services/base';

import _map from 'lodash/map';

…
export default {

namespace: ‘posts',

state: {

list: [],

total: 0

},

reducers: {

save(state, { payload: { list, total } }) {

return { ...state, list, total};

},
},
src/models/posts.js 2/3
effects: {

*fetch({ payload: { … }, { call, put }) {

let { list, total } = yield select(state => state.posts);

const graphQuery = `{

posts(first: 10) {

edges {

node {

id, _id, title, body,

type {

id, name, machineName

}

}

}

}

}`;
src/models/posts.js 2/3
#  Async call with Promise support.
const posts = yield call(postService.fetch, 'posts', { … });
const list = _map(posts.data.posts.edges, posts => posts.node)
# Action triggering.
yield put({
type: 'save',
payload: {
list,
total,
},
});
Directory Structure
src/
├── components
   ├── Auth
   ├── Generic
   ├── Layout
   └── Posts
      ├── ProjectBase.js
      ├── ProjectEdit.js
      ├── ProjectList.js
      └── ProjectNew.js
Thank you … Questions?

More Related Content

What's hot (20)

PDF
Cours design pattern m youssfi partie 5 adapter
ENSET, Université Hassan II Casablanca
 
PDF
La persistance des données : ORM et hibernate
Youness Boukouchi
 
PPTX
Présentation pfe Big Data Hachem SELMI et Ahmed DRIDI
HaShem Selmi
 
PDF
Rapport pfe 2017 Système de gestion des rendez-vous médicaux
fehmi arbi
 
PDF
Rapport application web (Spring BOOT,angular4) et mobile(ionc3) gestion des a...
MOHAMMED MOURADI
 
PDF
eServices-Tp1: Web Services
Lilia Sfaxi
 
PPSX
Langage RDF/RDFs
Rached Krim
 
PDF
Support de cours angular
ENSET, Université Hassan II Casablanca
 
PDF
PFE BI - INPT
riyadadva
 
PDF
Chp5 - Applications Android
Lilia Sfaxi
 
PDF
rapport PFE ingénieur génie logiciel INSAT
Siwar GUEMRI
 
PDF
Rapport-PFE2013-RahmaGhali-Gestion des Candidatures(Jaas,Primefaces,JFS2,JPA)
Ghali Rahma
 
PPSX
Méthode d'Agile: Scrum
ChaymaMghazli
 
PDF
Tp3 - Application SOA avec BPEL
Lilia Sfaxi
 
PDF
CONCEPTION ET REALISATION D ’ UNE APPLICATION WEB POUR GESTION DE P ROJETS DE...
Madjid Meddah
 
PDF
Rapport d'une application mobile de recommendation de livres
kaies Labiedh
 
PDF
Site JEE de ECommerce Basé sur Spring IOC MVC Security JPA Hibernate
ENSET, Université Hassan II Casablanca
 
PDF
Conception et développement d’une plateforme d'import-export avec paiement en...
Karim Ben Alaya
 
PDF
Rapport de pfe gestion de parc informatique et Helpdesk
Raef Ghribi
 
PDF
Spark SQL principes et fonctions
MICHRAFY MUSTAFA
 
Cours design pattern m youssfi partie 5 adapter
ENSET, Université Hassan II Casablanca
 
La persistance des données : ORM et hibernate
Youness Boukouchi
 
Présentation pfe Big Data Hachem SELMI et Ahmed DRIDI
HaShem Selmi
 
Rapport pfe 2017 Système de gestion des rendez-vous médicaux
fehmi arbi
 
Rapport application web (Spring BOOT,angular4) et mobile(ionc3) gestion des a...
MOHAMMED MOURADI
 
eServices-Tp1: Web Services
Lilia Sfaxi
 
Langage RDF/RDFs
Rached Krim
 
Support de cours angular
ENSET, Université Hassan II Casablanca
 
PFE BI - INPT
riyadadva
 
Chp5 - Applications Android
Lilia Sfaxi
 
rapport PFE ingénieur génie logiciel INSAT
Siwar GUEMRI
 
Rapport-PFE2013-RahmaGhali-Gestion des Candidatures(Jaas,Primefaces,JFS2,JPA)
Ghali Rahma
 
Méthode d'Agile: Scrum
ChaymaMghazli
 
Tp3 - Application SOA avec BPEL
Lilia Sfaxi
 
CONCEPTION ET REALISATION D ’ UNE APPLICATION WEB POUR GESTION DE P ROJETS DE...
Madjid Meddah
 
Rapport d'une application mobile de recommendation de livres
kaies Labiedh
 
Site JEE de ECommerce Basé sur Spring IOC MVC Security JPA Hibernate
ENSET, Université Hassan II Casablanca
 
Conception et développement d’une plateforme d'import-export avec paiement en...
Karim Ben Alaya
 
Rapport de pfe gestion de parc informatique et Helpdesk
Raef Ghribi
 
Spark SQL principes et fonctions
MICHRAFY MUSTAFA
 

Similar to Creating a modern web application using Symfony API Platform, ReactJS and Redux DrupalCampLA (20)

PDF
Creating a modern web application using Symfony API Platform Atlanta
Jesus Manuel Olivas
 
PDF
Building APIs in an easy way using API Platform
Antonio Peric-Mazar
 
PDF
API Platform and Symfony: a Framework for API-driven Projects
Les-Tilleuls.coop
 
PDF
API Platform: Full Stack Framework Resurrection
Les-Tilleuls.coop
 
PDF
REST easy with API Platform
Antonio Peric-Mazar
 
PDF
High quality ap is with api platform
Nelson Kopliku
 
PDF
A high profile project with Symfony and API Platform: beIN SPORTS
Smile I.T is open
 
PPTX
Creating hypermedia APIs in a few minutes using the API Platform framework
Les-Tilleuls.coop
 
PDF
API Platform: A Framework for API-driven Projects
Les-Tilleuls.coop
 
PDF
Building APIs in an easy way using API Platform
Antonio Peric-Mazar
 
PPTX
Graphql + Symfony | Александр Демченко | CODEiD
CODEiD PHP Community
 
PDF
Symfony + GraphQL
Alex Demchenko
 
PDF
Symfony 4 Workshop - Limenius
Ignacio Martín
 
PDF
Using API platform to build ticketing system (translations, time zones, ...) ...
Antonio Peric-Mazar
 
PPTX
API Platform: The Pragmatic API framework
soyuka1
 
PPTX
A soa approximation on symfony
Carlos Agudo Belloso
 
PPTX
A SOA approximation on symfony
Joseluis Laso
 
PDF
The Naked Bundle - Symfony Barcelona
Matthias Noback
 
PDF
The Naked Bundle - Symfony Usergroup Belgium
Matthias Noback
 
Creating a modern web application using Symfony API Platform Atlanta
Jesus Manuel Olivas
 
Building APIs in an easy way using API Platform
Antonio Peric-Mazar
 
API Platform and Symfony: a Framework for API-driven Projects
Les-Tilleuls.coop
 
API Platform: Full Stack Framework Resurrection
Les-Tilleuls.coop
 
REST easy with API Platform
Antonio Peric-Mazar
 
High quality ap is with api platform
Nelson Kopliku
 
A high profile project with Symfony and API Platform: beIN SPORTS
Smile I.T is open
 
Creating hypermedia APIs in a few minutes using the API Platform framework
Les-Tilleuls.coop
 
API Platform: A Framework for API-driven Projects
Les-Tilleuls.coop
 
Building APIs in an easy way using API Platform
Antonio Peric-Mazar
 
Graphql + Symfony | Александр Демченко | CODEiD
CODEiD PHP Community
 
Symfony + GraphQL
Alex Demchenko
 
Symfony 4 Workshop - Limenius
Ignacio Martín
 
Using API platform to build ticketing system (translations, time zones, ...) ...
Antonio Peric-Mazar
 
API Platform: The Pragmatic API framework
soyuka1
 
A soa approximation on symfony
Carlos Agudo Belloso
 
A SOA approximation on symfony
Joseluis Laso
 
The Naked Bundle - Symfony Barcelona
Matthias Noback
 
The Naked Bundle - Symfony Usergroup Belgium
Matthias Noback
 
Ad

More from Jesus Manuel Olivas (20)

PDF
Remix & GraphQL: A match made in heaven with type-safety DX
Jesus Manuel Olivas
 
PDF
Drupal 10 Party GraphQL
Jesus Manuel Olivas
 
PDF
How to use Drupal to create editorial experiences your content creators will...
Jesus Manuel Olivas
 
PDF
Beyond Static: Building a Dynamic Application with Gatsby
Jesus Manuel Olivas
 
PDF
Drupal, GraphQL, Views, View Modes and Gatsby for a US Gov site CMS Philly
Jesus Manuel Olivas
 
PDF
Embracing the modern web using a Headless CMS with GatsbyJS CMS Philly
Jesus Manuel Olivas
 
PDF
Embracing the modern web using a Headless CMS with GatsbyJS Stanford
Jesus Manuel Olivas
 
PDF
Building a modern application using Symfony API Platform and GatsbyJS PHP QRO
Jesus Manuel Olivas
 
PDF
Building a dynamic application with GatsbyJS-Tec-Mexicali
Jesus Manuel Olivas
 
PDF
Building a modern web application in the cloud partnercon
Jesus Manuel Olivas
 
PDF
Embracing the modern web using Drupal as a Headless CMS with Gatsby BADCamp
Jesus Manuel Olivas
 
PDF
Blazing fast sites using Blaze, Hybrid CMS NYC
Jesus Manuel Olivas
 
PDF
Embracing the modern web using Drupal as Headless CMS with GatsbyJS NYC
Jesus Manuel Olivas
 
PDF
Writing a slack chatbot seattle
Jesus Manuel Olivas
 
PDF
Building a Modern Web Application in the Cloud TecNerd
Jesus Manuel Olivas
 
PDF
How to keep Drupal relevant in the Git-based and API-driven CMS era Florida
Jesus Manuel Olivas
 
PDF
How to keep Drupal relevant in the Git-based and API-driven CMS era DrupalCampNJ
Jesus Manuel Olivas
 
PDF
Tools and Projects Dec 2018 Edition
Jesus Manuel Olivas
 
PDF
How to keep Drupal relevant in the Git-based and API-driven CMS era - BADCamp
Jesus Manuel Olivas
 
PDF
Battle of the CMS DrupalCampLA
Jesus Manuel Olivas
 
Remix & GraphQL: A match made in heaven with type-safety DX
Jesus Manuel Olivas
 
Drupal 10 Party GraphQL
Jesus Manuel Olivas
 
How to use Drupal to create editorial experiences your content creators will...
Jesus Manuel Olivas
 
Beyond Static: Building a Dynamic Application with Gatsby
Jesus Manuel Olivas
 
Drupal, GraphQL, Views, View Modes and Gatsby for a US Gov site CMS Philly
Jesus Manuel Olivas
 
Embracing the modern web using a Headless CMS with GatsbyJS CMS Philly
Jesus Manuel Olivas
 
Embracing the modern web using a Headless CMS with GatsbyJS Stanford
Jesus Manuel Olivas
 
Building a modern application using Symfony API Platform and GatsbyJS PHP QRO
Jesus Manuel Olivas
 
Building a dynamic application with GatsbyJS-Tec-Mexicali
Jesus Manuel Olivas
 
Building a modern web application in the cloud partnercon
Jesus Manuel Olivas
 
Embracing the modern web using Drupal as a Headless CMS with Gatsby BADCamp
Jesus Manuel Olivas
 
Blazing fast sites using Blaze, Hybrid CMS NYC
Jesus Manuel Olivas
 
Embracing the modern web using Drupal as Headless CMS with GatsbyJS NYC
Jesus Manuel Olivas
 
Writing a slack chatbot seattle
Jesus Manuel Olivas
 
Building a Modern Web Application in the Cloud TecNerd
Jesus Manuel Olivas
 
How to keep Drupal relevant in the Git-based and API-driven CMS era Florida
Jesus Manuel Olivas
 
How to keep Drupal relevant in the Git-based and API-driven CMS era DrupalCampNJ
Jesus Manuel Olivas
 
Tools and Projects Dec 2018 Edition
Jesus Manuel Olivas
 
How to keep Drupal relevant in the Git-based and API-driven CMS era - BADCamp
Jesus Manuel Olivas
 
Battle of the CMS DrupalCampLA
Jesus Manuel Olivas
 
Ad

Recently uploaded (20)

PDF
Generative AI vs Predictive AI-The Ultimate Comparison Guide
Lily Clark
 
PDF
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
PDF
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
PDF
Per Axbom: The spectacular lies of maps
Nexer Digital
 
PDF
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
PPTX
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
PPTX
Simple and concise overview about Quantum computing..pptx
mughal641
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PPTX
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
PDF
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PDF
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
PDF
introduction to computer hardware and sofeware
chauhanshraddha2007
 
PPTX
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
PDF
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
Tea4chat - another LLM Project by Kerem Atam
a0m0rajab1
 
PPTX
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
PDF
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
PDF
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
Generative AI vs Predictive AI-The Ultimate Comparison Guide
Lily Clark
 
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
Per Axbom: The spectacular lies of maps
Nexer Digital
 
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
Simple and concise overview about Quantum computing..pptx
mughal641
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
introduction to computer hardware and sofeware
chauhanshraddha2007
 
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
Tea4chat - another LLM Project by Kerem Atam
a0m0rajab1
 
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 

Creating a modern web application using Symfony API Platform, ReactJS and Redux DrupalCampLA

  • 1. Creating a modern web application using Symfony API Platform, ReactJS and Redux by Jesus Manuel Olivas / weKnow @jmolivas
  • 2. ● WHO AM I? Jesus Manuel Olivas [email protected] jmolivas jmolivas drupal.org/u/jmolivas jmolivas.weknowinc.com
  • 6. Symfony API Platform / GraphQL  ReactJS / Redux / Saga Ant Design
  • 8. Symfony Flex … a Composer plugin for Symfony > Symfony Flex is a Composer plugin that modifies the behavior of the require, update, and remove composer commands. > Symfony Flex automates the most common tasks of Symfony applications, like installing and removing bundles and other dependencies using recipes defined in a manifest.json file.
  • 11. The API Platform Framework REST and GraphQL framework to build modern API-driven projects https://api-platform.com/
  • 12. Built on the Shoulders of Giants > Extend the framework with thousands of existing Symfony bundles and React components. > The API component includes the Symfony 4 flex, the Doctrine ORM. Client-side components and Admin based on React and a Docker configuration ready to startup your project using one single command. > Reuse all your Symfony, React and Docker skills and benefit of their high quality docs; you are in known territory.
  • 13. The API Platform Components API Schema Admin CRUD
  • 14. Try API-Platform # Clone code repository
 git clone https://github.com/api-platform/api-platform.git
  • 15. Recommendations and adjustments > Update route prefix at api/config/routes/api_platform.yaml file. api_platform:
 …
 prefix: /api > Update admin/.env and client/.env files (change protocol and port). REACT_APP_API_ENTRYPOINT=http://localhost:8080/api
  • 16. Start containers … and grab water, coffee, or a beer. # Start containers ahoy up # Open browser open http://localhost/
  • 17. Add more formats Update api/config/packages/api_platform.yaml adding: formats: jsonld: [‘application/ld+json'] # first one is the default format json: ['application/json'] jsonhal: ['application/hal+json'] xml: ['application/xml', 'text/xml'] yaml: ['application/x-yaml'] csv: ['text/csv'] html: ['text/html']
  • 18. Add Dependencies > Add new entities to api/src/Entity/ directory:
 ahoy console make:entity
 
 > Remove default entity
 api/src/Entity/Greeting.php
  • 23. api/src/Entity/Post.php 1/3 <?php
 namespace AppEntity;
 
 use ApiPlatformCoreAnnotationApiResource;
 use DoctrineORMMapping as ORM;
 use SymfonyComponentValidatorConstraints as Assert; /**
 * @ApiResource
 * @ORMTable(name="post")
 * @ORMEntity
 */ class Post { … }
  • 24. api/src/Entity/Post.php 2/3 /**
 * @ORMId
 * @ORMGeneratedValue(strategy="AUTO")
 * @ORMColumn(type="integer")
 */
 private $id; /**
 * @ORMColumn
 * @AssertNotBlank
 */
 public $title = '';
 /**
 * @ORMColumn * @AssertNotBlank */ public $body = '';
  • 25. api/src/Entity/Post.php 3/3 /** * @ORMManyToOne(targetEntity="PostType") * @ORMJoinColumn(name="post_type_id", referencedColumnName="id", nullable=false) */ public $type; public function getId(): int { return $this->id; }
  • 26. Tracking Database changes # Add dependency ahoy composer require migrations # Execute command(s) ahoy console make:migration ahoy console doctrine:migrations:migrate
  • 27. Add FOSUserBundle # Add dependency composer require friendsofsymfony/user-bundle composer require symfony/swiftmailer-bundle https://symfony.com/doc/current/bundles/FOSUserBundle/index.html https://jolicode.com/blog/do-not-use-fosuserbundle
  • 28. Initialize the project > Drop and Create Database > Execute Migrations > Populate Entities with Data bin/console init Use:
 hautelook/alice-bundle 
 doctrine/data-fixtures
  • 29. Loading Posts using the Browser http://localhost:8080/api/posts http://localhost:8080/api/posts.json http://localhost:8080/api/posts.jsonld http://localhost:8080/api/posts/1 http://localhost:8080/api/posts/1.json
  • 30. Loading Posts using the CLI curl -X GET "http://localhost:8080/api/posts" -H "accept: application/json" curl -X GET "http://localhost:8080/api/posts/1" -H "accept: application/ld+json"
  • 31. ADD Posts from CLI curl -X POST "http://localhost:8080/api/posts" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" -d '{ "title": "Post create from CLI", "body": "body- less", "type": "/api/post_types/1"}'
  • 32. UPDATE and REMOVE Posts from CLI curl -X PUT "http://localhost:8080/api/posts/9" -H "accept: application/ld+json" -H "Content-Type: application/ld+json" -d '{ "title": "Updated from CLI"}' curl -X DELETE "http://localhost:8080/api/posts/10" -H "accept: application/json"
  • 33. Serialization > API Platform allows to specify the which attributes of the resource are exposed during the normalization (read) and denormalization (write) process. It relies on the serialization (and deserialization) groups feature of the Symfony Serializer component. > In addition to groups, you can use any option supported by the Symfony Serializer such as enable_max_depth to limit the serialization depth.
  • 34. Serialization Relations (Post => PostType) 1/2 # api/src/Entity/Post.php & PostType.php * @ApiResource(attributes={ * "normalization_context"={"groups"={"read"}}, * "denormalization_context"={"groups"={"write"}} * })
  • 35. Serialization Relations (Post => PostType) 2/2 # Add use keyword to class use SymfonyComponentSerializerAnnotationGroups; # Add use keyword to properties * @Groups({"read"}) * @Groups({"read", "write"})
  • 37. GraphQL GraphQL offers significantly more flexibility for integrators. Allows you to define in detail the only the data you want. GraphQL lets you replace multiple REST requests with a single call to fetch the data you specify.
  • 38. Add GraphQL To enable GraphQL and GraphiQL interface in your API, simply require the graphql-php package using Composer: composer require webonyx/graphql-php open http://localhost:8080/api/graphql
  • 39. Disable GraphiQL Update api/config/packages/api_platform.yaml adding: api_platform: # ... graphql: graphiql: enabled: false # ...
  • 40. Load resource using GraphQL { post (id:"/api/posts/1") { id, title, body } }
  • 41. Load resource using GraphQL form the CLI curl -X POST -H "Content-Type: application/json" -d '{ "query": "{ post(id:"/api/posts/1") { id, title, body }}" }' http://localhost:8080/api/graphql
  • 42. Load resource relations using GraphQL { post (id:"/api/posts/1") { title, body, type { id, name, } } }
  • 43. Load resource relations using GraphQL form the CLI curl -X POST -H "Content-Type: application/json" -d '{ "query": "{ post(id:"/api/posts/1") { id, title, body, type { id, name } }}" }' http://localhost:8080/api/graphql
  • 44. JWT
  • 45. JWT Dependencies # JWT composer require lexik/jwt-authentication-bundle JWT Refresh
 gesdinet/jwt-refresh-token-bundle
  • 46. JWT Events (create) # config/services.yaml
 AppEventListenerJWTCreatedListener:
 tags:
 - {
 name: kernel.event_listener, 
 event: lexik_jwt_authentication.on_jwt_created,
 method: onJWTCreated
 } 
 # src/EventListener/JWTCreatedListener.php
 public function onJWTCreated(JWTCreatedEvent $event)
 {
 $data = $event->getData();
 $user = $event->getUser();
 $data['organization'] = $user->getOrganization()->getId();
 $event->setData($data);
 }
  • 47. JWT Events (success) # config/services.yaml
 AppEventListenerAuthenticationSuccessListener:
 tags:
 - {
 name: kernel.event_listener, 
 event: lexik_jwt_authentication.on_authentication_success,
 method: onAuthenticationSuccessResponse
 }
 # src/EventListener/AuthenticationSuccessListener.php
 public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event) 
 {
 $data = $event->getData();
 $user = $event->getUser();
 $data[‘roles'] = $user->getOrganization()->getRoles();
 $event->setData($data);
 }
  • 49. dvajs/dva - React and redux based framework.  https://github.com/dvajs/dva
  • 50. React / Redux / Saga / AntDesig
  • 51. > Use webpack instead of roadhog. > Use apollo-fetch for GraphQL calls. > Use jwt-decode to interact with JWT. > Use LocalStorage for simple key/values storage. > Use IndexedDB for encrypted and/or more complex data structures. > Use Socket-IO + Redis to sync API with ReactJS client. Tips
  • 52. Directory Structure ├── package.json ├── src │   ├── components │   ├── constants.js │   ├── index.js │   ├── models │   ├── router.js │   ├── routes │   └── services └── webpack.config.js
  • 53. constants.js export const API_URL = process.env.API_ENTRY_POINT; export const GRAPHQL_URL = `${API_URL}/api/graphql`; export const NOTIFICATION_URL = process.env.NOTIFICATION_URL; export const NOTIFICATION_ICON = { info: 'info', warning: 'exclamation', error: 'close', success: 'check' };
  • 54. index.js import dva from 'dva';
 import auth from './models/auth';
 import local from './models/local';
 import ui from './models/ui'; const app = dva({
 …
 });
 
 # Register global models
 app.model(auth);
 app.model(local);
 app.model(ui);
 app.router(require('./router'));
 app.start(‘#root');
  • 55. router.js 1/2 import …
 import AuthRoute from './components/Auth/AuthRoute'; export default function RouterConfig({ history, app }) {
 const Login = dynamic({
 app,
 component: () => import('./routes/Login'),
 }); const routes = [{
 path: '/posts',
 models: () => [
 import('./models/posts')
 ],
 component: () => import('./routes/Posts'),
 }, { path: '/posts/:id',
 models: () => [
 import('./models/projects'),
 ],
 component: () => import('./routes/Posts'),
 }];
  • 56. router.js 2/2 return ( <Router history={history}>
 <Switch>
 <Route exact path="/" render={() => (<Redirect to="/login" />)} />
 <Route exact path="/login" component={Login} />
 {
 routes.map(({ path, onEnter, ...dynamics }, key) => ( <AuthRoute
 key={key} exact path={path}
 component={dynamic({
 app,
 ...dynamics,
 })}
 />
 )) }
 </Switch>
 </Router>
 );
 }
  • 57. src/components/Auth/AuthRoute.js import {Route, Redirect} from "dva/router";
 … class AuthRoute extends Route { async isTokenValid() {
 # Check for token and refresh if not valid 
 } render() { return ( <Async promise={this.isTokenValid()} then={(isValid) => isValid ? <Route path={this.props.path} component={this.props.component} /> : <Redirect to={{ pathname: '/login' }}/> } /> ); };
  • 58. src/models/posts.js 1/3 import {Route, Redirect} from "dva/router";
 import * as postService from '../services/base';
 import _map from 'lodash/map';
 … export default {
 namespace: ‘posts',
 state: {
 list: [],
 total: 0
 },
 reducers: {
 save(state, { payload: { list, total } }) {
 return { ...state, list, total};
 }, },
  • 59. src/models/posts.js 2/3 effects: {
 *fetch({ payload: { … }, { call, put }) {
 let { list, total } = yield select(state => state.posts);
 const graphQuery = `{
 posts(first: 10) {
 edges {
 node {
 id, _id, title, body,
 type {
 id, name, machineName
 }
 }
 }
 }
 }`;
  • 60. src/models/posts.js 2/3 #  Async call with Promise support. const posts = yield call(postService.fetch, 'posts', { … }); const list = _map(posts.data.posts.edges, posts => posts.node) # Action triggering. yield put({ type: 'save', payload: { list, total, }, });
  • 61. Directory Structure src/ ├── components    ├── Auth    ├── Generic    ├── Layout    └── Posts       ├── ProjectBase.js       ├── ProjectEdit.js       ├── ProjectList.js       └── ProjectNew.js
  • 62. Thank you … Questions?