React Admin
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWhoa! Look out! Bonus chapter! We know that our API is fully described using the Open API spec. Heck, we can even see it by going to /api/docs.json. This shows all our different endpoints and their fields. It gets this delicious info by reading our code, PHPdoc, and other things. And we know this is used to power the Swagger UI docs page. Our API is also described by JSON-LD and Hydra.
And, both of these types of API docs can be used to power other things.
For example, search for "react admin", to find an open source React-based admin system. This is super powerful and cool... and it's been around for a long time. And the way it works is... amazing: we point it at our API documentation and then... it just builds itself! I think we should take it for a test drive.
Search for "api platform react admin" to find the API Platform docs page all about this. This has some info... but what we're really after is over here. Click "Get Started". This walks us through all the details, even including CORS config if you have that problem.
So... let's do this!
Webpack Encore Setup
If you use the API Platform Docker distribution, this admin area comes pre-installed. But it's also easy enough to add manually. Right now, our app doesn't have any JavaScript, so we need to bootstrap everything. Find your terminal and run:
composer require encore
This installs WebpackEncoreBundle... and its recipe gives us a basic frontend setup. When that's done, install the Node assets with:
npm install
Okay, flip back over to the docs. API Platform has their own Node package that helps integrate with the admin. So let's get that installed. Copy the npm install line - you can also use yarn if you want - paste it in the terminal, and add a -D at the end.
npm install @api-platform/admin -D
That -D isn't super important, but I tend to install my assets as devDependencies.
UX React Setup
To get all of this working, ultimately, we're going to render a single React component into a page. To help with that, I'm going to install a UX package that's... just really good at rendering React components. It's optional, but nice.
Run:
composer require symfony/ux-react
Perfect. Now, spin over and search for "symfony ux react" to find its documentation. Copy this setup code: we need to add it to our app.js file... over here in assets/. Paste... and we don't need all of these comments. I'll also move this code down below the imports.
| // ... lines 1 - 12 | |
| import './bootstrap'; | |
| registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/)); |
Awesome! This basically says that it will look in an assets/react/controllers/ directory and make every React component inside super easy to render in Twig. So, let's create that: in assets/, add two new directories: react/controllers/. And then create a new file called ReactAdmin.jsx.
For the contents, go back to the API Platform docs... and it gives us almost exactly what we need. Copy this... and paste it inside our new file. But first, it doesn't look like it, but thanks to the JSX syntax, we're using React, so we need an import React from 'react'.
And... let's make sure we have that installed:
npm install react -D
Passing a Prop to the React Component
Second, take a look at the entrypoint prop. This is so cool. We pass the URL to our API homepage... and then React admin takes care of the rest. For us, this URL would be something like https://localhost:8000/api. But... I'd rather not hardcode a "localhost" into my JavaScript.
Instead, we're going to pass this in as a prop. To allow that, add a props argument... then say props.entrypoint.
| import { HydraAdmin } from "@api-platform/admin"; | |
| import React from 'react'; | |
| export default (props) => ( | |
| <HydraAdmin entrypoint={props.entrypoint} /> | |
| ); |
How do we pass this in? We'll see that in just a minute.
Enabling React in Encore
All right, let's see if the system will even build. Fire it up:
npm run watch
And... syntax error! It sees this .jsx syntax and... has no idea what to do with it! That's because we haven't enabled React inside of WebpackEncore yet. Hit Ctrl+C to stop that... then spin over and open webpack.config.js. Find a comment that says .enableReactPreset(). There it is. Uncomment that.
| // ... lines 1 - 8 | |
| Encore | |
| // ... lines 10 - 64 | |
| // uncomment if you use React | |
| .enableReactPreset() | |
| // ... lines 67 - 77 |
Now when we run
npm run watch
again... it still won't work! But it gives us the command we need to install the one missing package for React support! Copy that, run it:
npm install @babel/preset-react@^7.0.0 --save-dev
And now when we try
npm run watch
... it works! Time to render that React component.
Rendering the ReactAdmin Component
How do we do that? This is the easy part. In src/Controller/, create a new PHP class called AdminController. This is probably going to be the most boring controller you'll ever create. Make it extend AbstractController, and then add a public function called dashboard(), which will return a Response, though that's optional. Above this, add a Route() for /admin.
All we need inside is return $this->render() and then a template: admin/dashboard.html.twig.
| // ... lines 1 - 2 | |
| namespace App\Controller; | |
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | |
| use Symfony\Component\HttpFoundation\Response; | |
| use Symfony\Component\Routing\Annotation\Route; | |
| class AdminController extends AbstractController | |
| { | |
| ('/admin') | |
| public function dashboard(): Response | |
| { | |
| return $this->render('admin/dashboard.html.twig'); | |
| } | |
| } |
Cool! Down in the templates/ directory, create that admin/ directory... and inside, a new file called dashboard.html.twig. Again, this is probably one of the most boring templates you'll ever make, at least at the start. Extend base.html.twig and add block body and endblock.
Now, how do we render the React component? Thanks to that UX React package, it's super easy. Create the element that it should render into then add react_component() followed by the name of the component. Since the file is called ReactAdmin.jsx in the react/controllers/ directory, its name will be ReactAdmin.
| {% extends 'base.html.twig' %} | |
| {% block body %} | |
| <div {{ react_component('ReactAdmin', { | |
| // ... line 5 | |
| }) }}></div> | |
| {% endblock %} |
And here's where we pass in those props. Remember: we have one called entrypoint. Oh, but let me fix my indentation... and remember to add the </div>. We don't need anything inside the div... because that's where the React admin area will magically appear, like a rabbit out of a hat.
Pass the entrypoint prop set to the normal path() function. Now, we just need to figure out the route name that API Platform uses for the API homepage. This tab is running npm... so I'll open a new terminal tab and run:
php bin/console debug:router
Woh! Too big. That's better. Scroll up a bit, and... here it is. We want: api_entrypoint. Head back over, and pass that in.
| {% extends 'base.html.twig' %} | |
| {% block body %} | |
| <div {{ react_component('ReactAdmin', { | |
| entrypoint: path('api_entrypoint') | |
| }) }}></div> | |
| {% endblock %} |
Moment of truth! Find your browser, change the address to /admin, and... hello ReactAdmin! Woh! Behind the scenes, that made a request to our API entrypoint, saw all of the different API resources we have, and it created this admin! I know, isn't that insane?
We won't go too deep into this, though you can customize it and you almost definitely will need to customize it. But we get a lot of stuff out of the box. It's not perfect: it looks a little confused by our embedded dragonTreasures, but it's already very powerful. Even the validation works! Watch: when I submit, it reads the server-side validation returned by our API and assigned each error to the correct field. And treasures is aware of our filters. It's all here!
If this is interesting to you, definitely check it out further.
All right, team! You did it! You got through the first API Platform tutorial, which is fundamental to everything. You now understand how resources are serialized, how resources relate to other resources, IRIs, etc. All of these things are going to empower you no matter what API you're building. In the next tutorial, we'll talk about users, security, custom validation, user-specific fields and other wild stuff. Let us know what you're building and, if you have any questions, we're here for you down in the comments section.
Alright, friends! Seeya next time!
28 Comments
If you are also running into this error:
removing 'doctrine/annotations' might fix it:
To fix this I changed the composer.json to include "minimum-stability": "beta". Then I did a
composer update.Hey FR,
At what point did you get that error? Did you download the course code, or you're following the course by yourself?
I did download the course code and coded along. The error popped up in this chapter or the end of the last chapter.
Hey @FR!
There definitely IS some weird stuff happening right now, as the ecosystem transitions away from annotation - e.g. https://github.com/symfony/symfony/issues/48792
But I can't get this to repeat using the code - I've tried doing
composer installon thefinishcode as well as acomposer update. We MIGHT have a dependency that needs upgrading, but I can't trigger it. @FR - do you see the error when you runcomposer instalor do you need to actually use an endpoint?Thanks!
Thank you for the course!
You're welcome!
Hey there!
First of all, thanks for your nice Videos!
Really having fun here :)
Except, when it comes to ReactAdmin...
I tried it two times now with setting up the whole api from scratch (had to downgrade doctrine/orm to 2.18, because apparently 3.00 cant handle ManyToMany Relationsships anymore), BUT
I always get a white screen!
Apparently, react does something, if I inspect my element, this is what it looks like:
This is my twig template:
My ReactAdmin:
The only difference was, that I was using @babel/preset-react: 7.0.0 instead of 7.18.6
I tried to update the version, npm install, but still: white screen.
I see a loading circle for a short moment and I can see, that the system made 3 Ajax Requests:
/api
/api/docs.jsonld
/api/contexts/Entrypoint
You got any idea, how I can get it to work?
Thanks and greetings
Maik
Hey @Maik-D!
Apologies for my epically slow reply!
Hmm. Let's look through piece-by-piece:
This tells me that you're outputting the right stuff. And the fact that you see the 3 AJAX requests is what really convinces me that React has, at least, started. But no errors and nothing else on the page? Tbh, I can't think of how the
HydraAdminwould obviously start (HydraAdmincode is what makes those Ajax calls)...but then it doesn't do anything or show an error. Do the responses in those 3 Ajax calls look correct? Can you see that they're all returning JSON?Cheers!
Hi Everybody :)
First, I would like to thank you for this superb course.
Everything went good for me, except the last step,
npm install @babel/react-preset@^7.0.0 --save-devDidn't work : '@babel/react-preset@7.0.0' is not in this registry,
so I ran :
npm install @babel/preset-react@^7.0.0 --save-devThen, when loading the admin page, I got several JS errors/warnings, here are the first two logs :
which I couldn't resolve
Any idea ? I am on Symfony 6.2.14 by the way.
Thanks in advance :)
Hey @Hamza-Y!
Yikes! That is a huge, ugly error. To be honest, I have no clue what's gong on :/. I'd recommend running
npm outdatedto, perhaps, see if any packages are out-of-date. This certainly feels like different packages of different versions not playing nicely with each other - but I'm guessing.Sorry I can't be more helfpul!
Sadly the Hydra Admin is actually not working in Symfony 7.0.1, i also tested out 6.4 but i had still no luck setting it up there :(
Starting the moment an Entity has been added to the Api Platform (which works on the PHP site well) the Hydra Admin is crashing after loading.
Hey @Sebastian
Forgive my ignorance but is the Hydra Admin a project of ApiPlatform? Perhaps you could create an issue on their GitHub repository
Cheers!
I think that the React Admin from api-platform/admin is based on HydraAdmin (correct me if im wrong). I tested both because i wanted to know if the base of it makes the problem or api-platform/admin itself :)
If someone wants a (temporary) solution, here is a way to fix it:
https://github.com/api-platform/admin/issues/522
Yes, I think you're right.
It's a weird error, I don't know what could be the issue, but thank you for sharing a workaround!
Cheers!
Hey, React Admin is so cool, thanks for introducing it in this amazing course!
I am adopting it now, and I found out that HMR is currently NOT supported for React out of the box. I had to do some research to make it work with Symfony + Webpack Encore + React, and I wanted to share it here to help others save time and efforts.
What you need is React Refresh Webpack Plugin: https://github.com/pmmmwh/react-refresh-webpack-plugin/. Here's how to set it up with Webpack Encore:
If you use Symfony local webserver with SSL, you also need to enable HTTPS for
webpack-dev-server, otherwise you'll see error in the browser's console about refresh plugin not being able to connect to websocket:NOTE: this is the setup without Stimulus Bridge.
Ah, this is awesome! Thanks for sharing this - seriously!!
I am quite sure I'll come here myself a lot of times in the future after googling "how to enable HMR for react"😂
Hi, i'm user a project with Symfony 6.3.1, all work fine, but in ReactAdmin i have this error on my console :
I found the problem, i added my 2nd ApiResource before the first on my DragonTreasure class. I know now we have to add ApiResource AFTER the first one
Thank you for sharing your solution. Cheers!
Congratulations. One more masterpiece. Keep up excellent work.
Yo! Thanks for feedback we really appreciate it!
Cheers and Happy coding!
Hello!
In my admin panel when I go to create a user it only requires the username but not the password and the email, also when I want to create or modify an object it always gives me the error 422 because one of my assertions jumps without me having touched that field.
Any ideas?
Hi @Fran!
Hmm.
Do you mean that it doesn't show your email / password fields? Or that it shows them, but they are not required?
What do you mean by "the assertion jumps"?
But, I can maybe give some hints :). If you haven't done it already,
passwordwill need a validation group to be added - https://symfonycasts.com/screencast/api-platform-security/validation-groups - so that it isn't always required. This may or may not be your issue, but I wanted to mention it :).Cheers!
Hello,
How can i fetch owner(data who owner is email from token) data if i use JWT token ?
Regards.
Mmx
Hey @Mepcuk!
So you have a JWT and that JWT contains the
emailof the user? Is that correct? We'll talk closer to this use-case (though not this exactly) in the next tutorial. But I would:A) Use the new
access_tokensystemB) In the "access token handler" class you'll create, you'll decode the JWT to get the email
C) Then, return
new UserBadge($email). As long as your have an "entity" user provider insecurity.yamlset up to query from theemailproperty... that's all you need.Let me know if that helps :).
Cheers!
Hey @weaverryan!
I think you did not understand my question - I have a Get operation -
It's work perfect and i can get information if i know ID :))) If not resricted.
But i want to make resourse where i can get all data (relations) related to this user (i login via JWT token)
I try to get Collection but it not worked
Security key did not described in API=platform docs and i don't know how to fetch collection assigned-related to user with this JWT token. You told that in security: possible to write user check, but how?
Hey @Mepcuk!
Haha, that happens to me a lot - apologies :)
Let's see... Question: how are you using
GetCollectionoperation? Are you using anApiFilterto filter by email - e.g./api/loans?email=foo@example.com? Or something else?When you use a
GetCollectionendpoint, the actual "object" is not, of course, a singleLoanPersonobject, but an array (or technically aCollection) orLoanPersonobjects. You tried usingcollection.getEmail()- but I don't quite understand yet what you were trying to do.You also said:
If I understand correctly, you would like to be able to make a
GET /api/loansand receive back only the loans "owned" by the currently-authenticated user. Is that correct? If so, solving this is less about security and more about filtering the data (from a security perspective, all users will have access to fetch their collection of loans, but they should only see their own loans). For this, I would use a "query extension" - https://symfonycasts.com/screencast/api-platform-security/query-extension - to automatically filter this. It doesn't matter that the user is inside of a JWT. It only matters that you fetch the currently-authenticated user, then modify the query based on that user inside the query extension.Let me know if I was closer this time :)
Cheers!
"Houston: no signs of life"
Start the conversation!