We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Build A Blog in 15 (more like 30) Minutes: Webpacker Edition

00:00

Formale Metadaten

Titel
Build A Blog in 15 (more like 30) Minutes: Webpacker Edition
Serientitel
Anzahl der Teile
88
Autor
Lizenz
CC-Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 Unported:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen und nicht-kommerziellen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen und das Werk bzw. diesen Inhalt auch in veränderter Form nur unter den Bedingungen dieser Lizenz weitergeben.
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache
Produzent
Produktionsjahr2018
ProduktionsortPittsburgh

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
An ode to DHH's classic, let's build a blog with a Rails backend using a graphQL API and a React frontend. Through this live-coding session, you will learn how to set up an isomorphic app, the basic concepts of each library, and at the end have a fully functioning blog application! Follow along on your computer or clone the repo.
38
Vorschaubild
07:23
57
Vorschaubild
05:41
60
64
BootenSoftwareGebäude <Mathematik>Web logCoxeter-GruppeSoftwaretestFront-End <Software>IsomorphieklasseCloud ComputingProgrammbibliothekSoftwareschnittstelleAbfragespracheWurzel <Mathematik>DateiformatPatch <Software>MusterspracheEinfache GenauigkeitSehne <Geometrie>AbfrageGraphTypentheorieMereologieFaserbündelIndexberechnungSichtenkonzeptInhalt <Mathematik>MenütechnikSoftwareentwicklerFront-End <Software>DatenfeldSichtenkonzeptWeb-SeiteGebäude <Mathematik>ZeitstempelTypentheorieBildgebendes VerfahrenUmwandlungsenthalpieProgrammbibliothekREST <Informatik>sinc-FunktionAbfrageCASE <Informatik>Web logKartesische KoordinatenBenutzeroberflächeEDV-BeratungObjekt <Kategorie>Weg <Topologie>MultifunktionBenutzerbeteiligungInstallation <Informatik>MultiplikationsoperatorRoutingEinfache GenauigkeitOrientierung <Mathematik>InformationKonfigurationsraumOverhead <Kommunikationstechnik>App <Programm>GamecontrollerDatensatzZweiMereologieEndliche ModelltheorieGruppenoperationGewöhnliche DifferentialgleichungRechter WinkelFaserbündelGeradeCoxeter-GruppeGraphParametersystemVerschlingungDebuggingSoftwaretestKünstliches LebenDatenstrukturDiagrammComputeranimation
MIDI <Musikelektronik>Nabel <Mathematik>SichtenkonzeptThreadVersionsverwaltungATMKonfiguration <Informatik>ProgrammierumgebungNormierter RaumServerDefaultBinärdatenSchnelltaste
DefaultTypentheorieWeb-SeitePrototypingComputervirusVolumenvisualisierungZeichenketteDateiverwaltungElektronische PublikationApp <Programm>NormalvektorWurzel <Mathematik>DatenstrukturAutomatische IndexierungKartesische KoordinatenProzess <Informatik>BitSichtenkonzeptInhalt <Mathematik>Demo <Programm>Computeranimation
IndexberechnungOffene MengeComputervirusTypentheorieZeichenketteVolumenvisualisierungCAN-BusE-LearningPrototypingDefaultVektorrechnungRechenwerkGammafunktionAbfrageDatenfeldWeb logDatenfeldSichtenkonzeptTypentheorieElektronische PublikationVolumenvisualisierungFront-End <Software>MultiplikationsoperatorKlasse <Mathematik>InformationDefaultZusammenhängender GraphPunktWurzel <Mathematik>ProgrammbibliothekWeb-SeiteAbfrageKategorie <Mathematik>SoftwaretestZeichenketteFormale SpracheComputeranimation
Modul <Datentyp>RenderingGraphDatensatzMIDI <Musikelektronik>Nabel <Mathematik>SichtenkonzeptTypentheorieDatenfeldAbfrageMenütechnikFehlermeldungInformationKontextbezogenes SystemElektronische PublikationWurzel <Mathematik>VariableZeichenketteGraphische BenutzeroberflächeInhalt <Mathematik>ThumbnailBaum <Mathematik>VolumenvisualisierungDefaultBildschirmsymbolExogene VariableE-MailKreisringSpielkonsoleDigital Object IdentifierVektorrechnungDebuggingTypentheorieObjekt <Kategorie>Elektronische PublikationDebuggingProgrammierumgebungZusammenhängender GraphWurzel <Mathematik>AbfrageDatenfeldSoftwaretestRückkopplungDatenbankFunktionalReelle ZahlInhalt <Mathematik>MereologieWeb logKonfigurationsraumProgrammbibliothekMailing-ListeE-MailVolumenvisualisierungDatensatzp-BlockResolventeGamecontrollerOrdnung <Mathematik>RoutingSystemaufrufComputeranimation
VolumenvisualisierungMAPDefaultDigitalfilterGeradeQuellcodeSpielkonsoleExogene VariableDebuggingElement <Gruppentheorie>Graphische BenutzeroberflächeApp <Programm>Exogene VariableDebuggingLastVolumenvisualisierungSystemaufrufComputeranimation
Exogene VariableVektorrechnungSpielkonsoleVolumenvisualisierungAbfrageDefaultInhalt <Mathematik>E-MailDatentypMenütechnikBaum <Mathematik>IndexberechnungZweiVolumenvisualisierungAggregatzustandMereologieExogene VariableSystemaufrufZusammenhängender GraphNormalvektorFunktionalDatenbankMAPOrdnung <Mathematik>Computeranimation
KreisringMarketinginformationssystemVolumenvisualisierungExogene VariableIndexberechnungEndliche ModelltheorieMultiplikationsoperatorWeb-SeiteInhalt <Mathematik>BetragsflächeRouterComputeranimation
Graphische BenutzeroberflächeInhalt <Mathematik>IndexberechnungMAPBimodulElement <Gruppentheorie>VolumenvisualisierungExogene VariableEndliche ModelltheorieE-MailAbfrageSpielkonsoleDefaultVerschlingungDatentypAggregatzustandElektronische PublikationVerzeichnisdienstCloud ComputingCursorZeiger <Informatik>VektorrechnungElement <Gruppentheorie>Abgeschlossene MengeDatenbankWeb logVerschlingungDatenstrukturFront-End <Software>Zusammenhängender GraphApp <Programm>AbfrageInhalt <Mathematik>FunktionalEigentliche AbbildungWort <Informatik>SystemaufrufBitDatensatzDifferenteComputeranimation
Modul <Datentyp>LastProzess <Informatik>Quelle <Physik>SichtenkonzeptNabel <Mathematik>BitrateDatenfeldSpezialrechnerFunktion <Mathematik>TypentheorieEin-AusgabeZeichenketteGammafunktionMessage-PassingInformationsmanagementGraphische BenutzeroberflächeAbfrageVariableSyntaktische AnalyseEndliche ModelltheorieDatensatzTextbausteinDatenbankInformationExogene VariableWeb logE-MailVerschlingungDefaultIndexberechnungInhalt <Mathematik>VolumenvisualisierungVektorrechnungNormalvektorWechselsprungElement <Gruppentheorie>QuellcodeDigitalfilterBootenFlash-SpeicherAutomatische HandlungsplanungObjekt <Kategorie>ParametersystemDatenfeldKategorie <Mathematik>Exogene VariableSystemaufrufVerschlingungDatensatzDivisionTypentheorieAggregatzustandMultiplikationsoperatorWurzel <Mathematik>VererbungshierarchieMusterspracheMessage-PassingSpielkonsoleZusammenhängender GraphDefaultSchlüsselverwaltungZweiEin-AusgabeAutomatische IndexierungGenerizitätZeichenketteDebuggingEinfache GenauigkeitAbfrageFunktionalWeb-SeiteCodeBimodulÄhnlichkeitsgeometrieWeb logProgramm/QuellcodeComputeranimation
Quelle <Physik>Prozess <Informatik>Modul <Datentyp>LastSichtenkonzeptNabel <Mathematik>Normierter RaumRFIDElement <Gruppentheorie>QuellcodeExogene VariableDigitalfilterDefaultGraphInhalt <Mathematik>Hill-DifferentialgleichungDämon <Informatik>RechenwerkGraphische BenutzeroberflächeVerschlingungMessage-PassingSpielkonsoleVolumenvisualisierungIndexberechnungMAPMailing-ListeDebuggingSystemaufrufKategorie <Mathematik>VererbungshierarchieFunktionalZusammenhängender GraphComputeranimation
Element <Gruppentheorie>QuellcodeExogene VariableDigitalfilterMAPDefaultGraphische BenutzeroberflächeEndliche ModelltheorieSyntaktische AnalysePartielle IntegrationNabel <Mathematik>Modul <Datentyp>LastSichtenkonzeptMessage-PassingE-MailAbfrageVerschlingungLokales MinimumMultiplikationsoperatorComputeranimation
Modul <Datentyp>LastSichtenkonzeptInhalt <Mathematik>RenderingDatensatzNabel <Mathematik>Verzweigendes ProgrammComputeranimation
Inhalt <Mathematik>E-MailEreignishorizontDefaultBildschirmmaskeSchnittmengeEndliche ModelltheorieVolumenvisualisierungEin-AusgabeFreier ParameterWeb logMathematikSpezialrechnerGraphCOTSKategorie <Mathematik>ClientMathematikZusammenhängender GraphInhalt <Mathematik>AggregatzustandEin-AusgabeInformationBildschirmmaskeVerzweigendes ProgrammComputeranimation
AggregatzustandInhalt <Mathematik>E-MailBildschirmmaskeDefaultEreignishorizontTypentheorieZeichenketteDatentypExogene VariableAbfrageDatenfeldEin-AusgabeInhalt <Mathematik>ResolventeParametersystemComputeranimation
Cloud ComputingNabel <Mathematik>TopologieMIDI <Musikelektronik>SichtenkonzeptWeb logGraphische BenutzeroberflächeEndliche ModelltheorieMultigraphDialektDatenbankRouterKartesische KoordinatenWeb-SeiteBestimmtheitsmaßDemo <Programm>Computeranimation
COMp-BlockDatentypXMLComputeranimation
Transkript: Englisch(automatisch erzeugt)
Okay, cool, that sounds like I'm mic'd, for sure. Hi guys, sorry we're getting started a little late. Technical difficulties at a tech conference, makes sense.
So, hi, I'm Sasha, Sasha Grodzins. I'm a Chicago native, a Dev Bootcamp graduate. And I've been working as a developer at a consultancy called Dev Mind Software, also in Chicago and San Francisco. If you wanna find me online, I am sassyrody pretty much everywhere.
Okay, welcome. Thanks for coming to Build a Blog in 15, more like 30 minutes. This is the Webpacker edition, and I also just added GraphQL to the title because that's what we're using, which has been a common theme throughout the past few days.
Another common theme I've found is a theme of nostalgia about Rails, with the history of Rails track and all that. So, this also fits in there. It's an ode to DHH's presentation of the same name, where he presented Ruby on Rails to the world back in 2005, I believe.
So, there he live codes this presentation where he builds a blog with posts, comments, pagination, and adds some tests at the end, and he does it in 15 minutes. So, that's been blowing people's minds for over a decade and has been a tutorial and reference since. So, what is this talk?
This talk, we're also going to build a blog. It's not going to be as revolutionary or fast, hence the title, and we're gonna be using a React front end, a GraphQL back end, and I'm gonna show how you get that all hooked up in a single Rails application. That link is not active yet, so sorry about that.
Just ignore it. It will be on GitHub as soon as I'm off the stage. Okay, so welcome web pack. For the past few years, my company has been building with these tools, and I remember the first time we set up an app,
we had a front end app and a back end app. One was React and one was our Rails API, and it took a full work day, if not longer, to get it all hooked up and talking to each other and displaying data on a page just because the configuration added so much overhead. But then Rails 5 was released, and Webpacker made its debut and changed everything.
You could initialize a Rails app with pretty much no configuration and have React, or I'm sorry, we could have any JavaScript library just configured to run your application. So that was great.
I have to be honest, I'm working without my speaker notes, so that's why I'm kind of struggling right at this moment. But anyway, yeah, Webpacker came out, and it was awesome. I think that's all I had to say about that. So what are we doing? I wanna just go over what our tools are right now, so we're gonna use React to build all of our views.
The definition of React from the docs is a JavaScript library to build user interfaces. Yeah, it's a view library, it is the client, it is a front-end for all those things. What is GraphQL? GraphQL, again, from the docs, a query language for your API.
What does that mean? I think the thing to think about here is that React and GraphQL were created by Facebook, and they, as they're becoming one of the biggest companies in the world, faced some problems with traditional REST APIs and jQuery madness kinds of things, so they created these libraries.
The most notable thing about GraphQL is that it uses a single endpoint. This is not a single endpoint, this is a RESTful API. It's a screenshot straight from the Rails doc, so it should look familiar. So yeah, the way RESTful APIs work is you have different endpoints for each action you want to perform
and each view you wanna show. So if I wanted to get a single article, I'd just have to make a request, an HTTP request, to get article slash ID, pass the ID into the parameter somewhere. And what I expect back from that, from a Rails application, is a full active record object,
which would be the ID, any fields I migrated onto it and created that updated timestamps. So that's a lot of data, and I might not always need that. So GraphQL solves this in having a single endpoint. So here we see post slash GraphQL. That is the only way to get data
in and out of your backend. So the way that works is you make your HTTP request to the single endpoint, GraphQL, and you pass in a GraphQL query that looks exactly like this, shaped like JSON-y kind of structure.
So if I wanted to get the same data that I just mentioned before from a graph API, I'd say I need a post with an ID, and then the specific fields of data that I want to display on the page. So in this case, I'd only get a title back. I wouldn't get ID or timestamps or anything else from that object.
So that's really nice, because it is small, contained pieces of data, and it makes everything very predictable. How does it work? This is a big talk, so maybe you've been to a GraphQL talk in the past few days, and if you have, that's awesome.
But just a little overview, there's the idea of types of mutations in GraphQL. So a GraphQL type corresponds to some piece of information in the backend that you want to expose to the frontend. So in our case today, it's going to correspond to an active record model. The mutations specify which parts of the data
in the backend you want to change. So because you only have a post request that you can make, you have to say whether or not you want to get data or you want to change data via a type of orientation. Okay, so the other reason this isn't going to be as fast and cool as DHH's talk
is because I've already done some setup. I did the Rails new command with the webpack extension, where we're gonna use React as our frontend. That took close to three minutes to run in this Wi-Fi, so I'm glad I did that. I also added the GraphQL library in the gem file,
and I bundled, hopefully everyone's seen a bundle. It's not very exciting. I did run the Rails ggraphql install command, which is actually a really cool command. I'm sorry you don't get to see that today. But that's the thing that basically sets up the whole API. It adds a line to our routes for this post GraphQL endpoint.
It sets up the controller with a single method for that endpoint to hit, and it sets up some route type and mutations for us. Pretty nice, we'll see all that in a second. I added a model already, the post model, and I seeded some data because you also don't need to watch me do that. And, ooh, is that so loud?
I added a single view page so that when we hit localhost slash route, we don't get the Rails friends image. It doesn't matter how cute it is. We don't need it. All right, that's it. That's it for these slides. Get out of here.
All right, the first thing I wanna do is I wanna start the server. So because we have our webpack server and our Rails server, we have to start both. So we'll say Rails server. How's that font size, everybody? You good? You good? Nice, nice, thanks. Okay, and then on this side,
we're gonna use the built-in command, which is dot slash bin slash webpack dev server. You can add a shortcut in your package JSON to get a faster, shorter thing, but I'm gonna keep everything as default as I can. Okay, so it looks like those, ooh, oh my god, that's a lot of tabs.
How embarrassing. Okay, cool. So local host is up and running. It says hello from the index. I added that to my single Rails view page. So if I go to my views, I've got this index.
Hello from the index. So this is a normal Rails view. It's ERB with a little bit of text in it. But like I said, I want all of our views to be built using React. So I'm gonna go try to find my React files.
Is this, this is probably like impossible to see, so sorry, but on the side, we've got our normal app structure in this file system tree, but the webpacker command at the installation process added a JavaScript folder, and it has a single folder called packs and two files in it. The one we're gonna be using today
is called hello react. So this was just generated for me. I haven't touched this since I ran Rails new. And this is a React root note is what we like to call these where you are finding a document, appending stuff, and that's where all of your React content is going to live.
So they've got this demo here, and it says at the top grab this tag and put it in any of your layouts. So I'm gonna put it in this application thing that I'm calling my layout. So JavaScript back tag, hello react.
So if I refresh the page, I get a new piece of information there. It says hello react, and that's coming from the component that was generated for me. So here's this hello, and it's like calling props.name, and name is sent down here, so it's like hello RailsConf.
Save it, go back. I didn't hit refresh. It's hot reloading for me, which is also really great. Hello RailsConf. So basically what I'm just proving here is that React is loading, and I didn't do anything really, which is awesome. The next thing I wanna do, so we're building a blog, right?
I need to build a blog container. So I'm gonna make a file called blog container, just .js. The other thing I'm gonna do a lot of to speed up time is use Snippet, so bear with me. You don't need to see me type out all these words, and the fat finger everywhere. There will probably be some typos,
so watch out. All right, so I'm going to define a ES6 class called blog container that extends React component. I'm importing component from the React library itself, so this is a React component and what they look like. They have this render function, because the whole point of a React component
is to render stuff. So this is just a JavaScript function, and it has this explicit return, because we're in JavaScript. And this, what it's returning is called JSX, and JSX is the templating language used in React. It looks a lot like HTML, so it's really easy to write in,
but it has full JavaScript capabilities and a lot of built-in properties that are very helpful to us. So I have defined this blog container, but I'm not rendering it anywhere, so I want to render it into my root node,
my blog container I'm gonna import from blog container. And the stuff like, I mean, I'm just gonna delete it for now, we don't need it. Instead of rendering the generated little tiny component, I'm going to render my blog container. So at this point, we should see
whatever's in the blog container on the page. Yeah, okay, great, it's very good. It looks still so bad, because of this thing, I'm gonna get rid of this. And now, we have a full, I mean, our only view is a React view. So we're gonna just continue to build only in the blog container.
So what's a blog without post? It's nothing. So we need to find a way to get some data onto this page, and the way we're gonna do that is by making a request to our GraphQL backend from our client here. So we need to go back to our GraphQL
and set some things up. So as I mentioned, the GraphQL installation added this GraphQL folder, and then two folders within for mutations and types. That's where we're gonna keep our data, our definitions of these GraphQL types.
So if I look here, there's a query type, mutation type, with a bunch of test fields. We could play with those, but I'm gonna skip over them for now. What I need is I need something, I need a new type. These things are working off of default type strings, so this test field is returning
some kind of default GraphQL type, a string. As you can see here, that string is hello world. So I need to define a new type. Mentioned, everything is built off of types and mutations. So there is luckily a command for this as well. No, a GraphQL object.
So this will be a post, capital P post, with a title. These things are going to correspond to what's in my database, because that's what I want to be available to me on the front end. Okay, cool. So it made a single file called post type. I'm gonna go look for that, here it is.
So it just generated this big thing for me, which is awesome, and I know that I have a post type now, a GraphQL type called post type, and it has these fields on it. So what do I need to do with them? I need to expose them to the root layer of my API. So the way you do that is you define a field here,
and whatever you wanna call that. I'm gonna call it posts. I want a list of posts, so I'm gonna say, this new type I just added is post type, and then part of the DSL, like the GraphQL library, comes with this to list type method. So that'll basically just return an array of post types,
however many I'm gonna get in my resolve block. Oops, not bad, list of posts. So the other really cool thing about GraphQL is that it's pretty much self-documenting based on just what I write here. You know what you're gonna get, because I just said I'm gonna return a list of posts.
So this is a resolve block, it's a lambda, and it has these three things that are coming back from the GraphQL controller itself. But it doesn't really matter, because all I want from here is an active record call that is post.all. I want all the posts, and I don't care what order they're in, I don't care anything about them. It's what I want.
So there's another really cool thing about GraphQL. I don't know if I mentioned this yet, I can't remember. But it did also initialize with this other route called GraphEQL, or GraphiQL, whoever you are, however you wanna say it, it's fine. And this is an in-editor IDE for you to test out
your queries that you write. So like the query we just wrote. Is that okay? Okay, okay. That's the best kind of feedback. Okay, cool, so well, I feel like I hadn't written that yet.
So this is a documentation explorer. That's a great name for it, so that's exactly what it is. You see it has your root types here, query and mutation. So I can click into this and say like, what do I have available to me in my GraphQL API? That is posts, two fields. There's posts and there's test field. And we can see over here on this root query type
that we have these two fields, post and test field. So like I said, I don't think I've mentioned anything about this. This is a GraphQL query over here. This is exactly what we're going to ask the database for, so I start typing, it's very, very helpful
in that it tells me what I can type, basically. Like I can type an A and it'll say like, oh, there's nothing with that, what do you want? Really? And it'll be like, I want titles. So it looks like I'm getting some data, right? There are a couple of blog posts that I seeded as I mentioned. Two of them are just things that I wrote.
One of them is a real blog post from the Dev Find blog. So that's great. Like this is a JSON looking object. I feel like I know how to work with that and I'm only getting titles, which is exactly what I asked for. If I wanted content as well, I'd have to put
content in there explicitly so that the API knows what to get. You know, we could get all three of the things I need, but I really think this is some titles right now. Yeah, so what do we do with this type? What do we do with this query? I'm actually trying to remember.
So we're going to go to our JavaScript again. We're going to flip back to our React. This is our blog container. It doesn't do anything and really what we need it to do right now is go make a request to our API. So I'm going to define a function called get all posts.
So these functions are available to like within the component. They're just scoped functions to this container component. So get all posts. I didn't want to add any more libraries. This is just the basic config. So we're going to use the fetch API.
Here's a nice big snippet for you. So the fetch API is just built into React right now. So it's great. So you can make any kind of HTTP request with the fetch. You just specify the URL here, GraphQL. The method, we know it's going to be a post. Headers and then in the body, this is where
you're going to set your GraphQL query. So you just kind of give it a key, give it a string, and then I'm going to literally copy this GraphQL query I was playing with in GraphQL and paste it. That's what I'm going to do.
And then I'm going to, you know, resolve the promises, fetch from the promise. You have to resolve it once to JSON and then you resolve it again and we'll see what we get in this last part here. Ooh, okay, so I'm not hitting the debugger
because I'm not actually calling this function anywhere. So where do I want to call this function? I want this data and this fetch request to have been called before the page renders. So components have these things called lifecycle methods. We're going to use component did mount.
There are a couple of them and they're totally worth reading about and they're very helpful. Component did mount and I'm going to have component did mount called get all posts. So this will call before, ooh, oh, so fast. It'll call before the render loads as you
kind of just saw there. Like it's just like pops open here. So I'm in my debugger. This is too big to even operate in. What do I have? So I have this response data and it's got posts. Yes, okay. So I've got an array of posts coming through at the end of that fetch which is awesome
because that's exactly the data that I want to show in my, you know, in my app. So what do I do with this data? I'm here. I've got response data posts. That's an array. So I need to have that accessible to the render method.
The way I'm going to do that is use another built-in React function called state or a React property, I'm not really sure what you want to call this. But state is built into every React component as well. So you can pretty much hold anything you want in state. I'm going to hold posts here and just set it as an empty array.
So this is like my initialization of the state of the React components. Like there's nothing there. I can't just call this dot get all posts here because that's not how state works. So the order in which things are going to work is the component's going to load from the root node. It's going to call state. State will be set to this dot state dot posts
as an empty array. Component did mount is going to call next and it's going to call this dot get all posts. That'll call our fetch method and at the very end we're going to reset the state. Reassign that empty array into actually having data. So we'll say post response data posts.
So then state is available to every part of the component. So I can say not that. Post equals this dot state dot posts. So this const should be an array of those posts from the database. And then I'm going to just iterate over them
in the JSX. So to evaluate any JavaScript you use two curly braces in JSX. And we can just use a normal ES6 map for a single post and give it an index. Oh no, that's going to be too big.
Oh no. All right. So then it looks like HTML. It's not HTML. It's JSX. So you do curly braces to evaluate the post title. Cross your fingers guys. What do we have? What do we have? Oh yes. We've got a list of titles.
Oh yeah. Okay great. So like that still feels good. I've been doing this for a really long time and it still feels very good and like such a relief. Okay. What do we want to do? Actually I want to show the post content as well. So like everything's going to be on a single page.
I don't have a router so we're not going to have like a show page. It's just all going to be right there on the single page. So I'll do this. Okay. Great. Adjacent JSX elements must be wrapped in a closing tag. Learning a little bit about JSX here. That just means that you can't have two siblings like right there next to each other.
They need to be wrapped in something. I'm going to wrap them in a div. You can wrap them in whatever you want and then that should load properly. But we don't have any content. Maybe you know why but the reason we don't is because I'm not asking for it yet. The GraphQL query is like you didn't ask for
content. I'm not going to give you any content. So I need to put it in the query and I have to come back. Now we have some content. So that looks like garbage but there's no styling. There's not going to be in this talk. So just hold tight, you know. At least there's data.
Cool. Okay. So we are rendering blog posts. I think that's really great so we know how to get database from our back end. Now we need to learn how to change some data. So I know this is called a CRUD app but we're going to go out of order and we're going to do the D of CRUD first. So we're going to delete.
Really like when it comes down to it the other thing about the GraphQL is that you only have a mutation and a query. You don't have CRUD. You don't have gets and posts and puts and patches and deletes. You only have one thing. So it's not exactly CRUD anymore. The definition of CRUD is a little different here.
But let's do this. What am I doing? I need a delete link. So I know that the delete link itself is going to make another fetch request. So just to keep it like contained I'm going to make a new component for delete link, call it
delete link and I'm just going to put the word delete there and I'll give it a class. Which is, you know, link. Then I'm going to render it. I'm going to render it in the blog container import delete link.
I'm going to render it right below the post title. Oh yeah. Okay. So now each row of posts has a delete. It does not look like a link. I'm going to just very quickly change that
because I think that's too crazy. That link will say links are usually blue. So the other really cool thing about having a React app in your Rails app is that you get the asset pipeline which we all know emplove
and I like that a lot. So cool. This thing doesn't do anything. It's a div. It's styled to look like a link. What I want to do is give it an on click handler to call a function called like handle delete.
I'll define handle delete as a function again in this component and we're going to make another fetch request. So the same thing, the same structure but now we need to make a different kind of query. Essentially here we need to make a query for mutation and we don't have any mutation
so we have to go back to GraphQL now. Let's see, GraphQL mutations. Yeah, mutations are empty. So I'm going to create GraphQL mutation and it's going to be delete underscore post.
That's just the way it's going to be. So that added a couple of things here for us. It added a file under mutations, delete post. So this is not the same kind of definition we've been seeing. The post type itself was just a GraphQL object type
whereas the delete post mutation is built off of a different module. But it looks kind of similar in a lot of ways so it's actually really nice. The other thing it did is it added, oh, I'm just going to delete this. Yeah, I'm going to remove you. It added the field for me on the mutation root type.
So delete post has a field now on this mutation type. Okay, so let's see. This thing has directions for me. It says to do, define your return fields. So it's suggesting I return a post but because I'm deleting here I'm actually
just going to return a message and I'm going to use the default GraphQL type of a string because I'm just going to say at the end of this, if all goes plan message post was deleted. That's what I want to see somewhere, probably in a flash message eventually but maybe just in the console for now.
And then the next thing is to do, define arguments. So input field name type string. This also doesn't feel quite right because when we delete we usually delete by finding things by ID and then deleting the object that we found. So the input field is going to be an ID and again, we can use the generic GraphQL types of ID
to do, resolve the function. So again, let's just do some active record stuff. Let's find the ID which I'm going to pass through in the args, kind of like how we saw in the query before and then we'll just, I'm just going to destroy that post. No questions, did I spell that wrong?
Destroy, cool. D story happens to me all the time. Okay, yeah, so we're just going to assume that works. I'm not going to, I'm going to put the thing. So let's see, we can use GraphQL to test that out again if I refresh this page.
Look at our mutations, we have a single field for delete post and it takes input. So I think the same way that I created the query over here, I can do the same. Start typing delete post, input, and input type is an ID, right? So I'm going to delete the first one, let's see.
Okay, so I just took a guess there. I assumed there was a post with the ID of one. But it does say that the post was deleted. So if we go back to our front end and we refresh, there are only two, which is exactly what I wanted.
Cool, so I'm going to do the same thing, where I copy this mutation, put it into the delete link query. I want to hard code this, so like somehow
I need to get the post ID. The way I'm going to do that is if I go back to the blog container, we're iterating through these posts, right, so this is like a post row and the div itself is contained to a specific post. So I can send in a property called post ID,
which would be post.id. This isn't going to work immediately because I'm not requesting the ID. So if I want to do that, I have to put it here. So now I'm calling all three of the fields
that are available to me. I'll have that in my response and I can send it through to the delete link. So the delete link can access these kind of properties via a call that looks like this, this.props.post ID. So that's how you can pass data
from your parent down to your child and that's a very common pattern in React, like precisely for this kind of demonstration. Let's see, so we're going to get our response back. Hopefully it's good. Hopefully we get a message here. So we'll console out the message. We don't really have state in this component.
This component is just a link, so I'm not going to try to set the state here, but I do really want to reload the posts at the end of this because I don't want to hit refresh when my posts are deleted. So I can actually just demonstrate this. Let's see where we're at here.
I open this. Oh, okay. Well, that's just a warning. I'll fix it in a second. If we hit delete, uh-oh. Got an undefined because it didn't work, right? Oh, it did work. Wow. Okay, so now we only have one post. The delete is working. I am surprised.
Mostly I'm just thrown off by that huge, gross warning. So I'm gonna fix that. That warning is saying that when you iterate over things, you need to pass in a key with a unique value. So I'm gonna use the index of the map. That'll be pretty unique. So that warning is gone, which is great. If I hit delete again, hit refresh,
now I don't have any more posts to work with, so I have to recede. Give me a sec. Okay, great. I'm actually gonna just see it again. Let's do it. Let's get a bunch of them. Cool, okay. So we have six. Can I count? Yeah.
Okay, so right, I'm hitting delete, and then I have to hit refresh to get a new list of posts back because the front end doesn't know what to do yet. So if I go back to my delete link, I've already written this function somewhere, right? The this.getAllPosts.
But that is not available to this component because that is defined in its parent. So much like how we passed down the property for post ID, I'm gonna pass down this function as a property. So it's called getAllPosts, this.getAllPosts. So then I can go back to my delete link, and again, call it via props.getAllPosts.
So you can pass pretty much anything down from a parent to a child via props there. That's a function. We pass strings. You can pass whatever you want. Let's see. Let's see where we're at. If I close that and hit delete, yes, woo!
Okay, so that thing is deleted out of the database, and I'm gonna do a couple more because there are too many. Awesome. That's deleting. Yep, I'm like stunned because I still have time, and like every time I've gone through this, I've run out of time before I got here.
So cheers to talking really fast and for things going pretty well. Let's see. I guess we should just keep going. We may as well do create. Oh, I don't know. That sounds like kind of ambitious, but let's try it.
Let's see how far we get. I'm going to, yeah, I'm just gonna like check all this out. So everything on here is going to be in a branch eventually. I'm sorry, not a branch, a git repo. Everything is like branched out pretty precisely,
so if you wanted to check out different spots of the tutorial that will be available, you could start from like wherever you wanted. They build on each other, so like in this tutorial, it's gonna go C first, C of crud to, or R of crud, C of crud to U of crud to D of crud. I find that to be like the most normal way
to build your application, but I started with D today, because why not spice it up? But if we want to see C, let's just like check out C of crud start. No, let's just see what it looks like completed. Why not?
Let's go through all the branches. Okay, so here I did add a form. This form lives in its own component. So again, it's just a little component. It has some state for title and content.
This is like exactly how the docs of React tell you to make a form, so there's plenty of information about like why this is how it's set up, but forms, again, look a lot like HTML, but they have all of these handlers and properties that help like set the state and make changes and submits data.
So these inputs, this on change where a client is handled, input change and handle input change is just resetting the state. So for us, when we are creating our mutation, we're gonna send along the title and content from the state that is the title and content that is here, so like, oh, right, okay.
So nothing's happening because we don't have a create mutation, so let's see, oh, shoot, yeah, we do. I forgot. Okay, so I've already created my create mutation. Let's just go look at it. Create post, so I did this the same way
I made my delete post, which was brails-g GraphQL mutation, create posts. This one, you can see like, has a return field for the post type, unlike delete, which was a return field of a message, I think is what I did. This one has input field for title and content,
and here, the resolve block, again, active record, whatever you wanna do from your mutation is we're gonna create a post. So we create a post with the title and content that comes through in the arguments again. And actually, that's like, oops, invisible to us here. This fetch has a mutation called create post title content.
They all start to look a lot alike, if you can't tell already. Let's see, let's check out. So this might be the end of it.
This might just be like everything working. So we have this delete, delete things, and I can edit. So the way I chose to edit for the sake of this demo was to do it on the same page, right? Like I said, I don't have a router.
I don't wanna like, have a page just to view and edit form. So you can edit and, oh. Tips for petting every dog on the street or something like that. I wrote that. You can edit this, just be like, woo. That says phew, but you know, I do mean woo.
And then it edits in place. So that is more or less the completed CRUD application that I meant to demonstrate today. And that's it, actually. So thank you so much for coming. Guys, I had a blast. I hope you did too. Thank you.