We discuss developing at scale with NxMonoRepos, which provide the benefits of a monorepo without the drawbacks of code collocation. Nx helps with code consistency and automation through plugins and generators. The workshop covers folder structure, workspace creation, using plugins, updating files, using executors, libraries and migrations, running generators and migration scripts, connecting APIs, creating type libraries, and using generators for Storybook configuration. It also covers configuring Storybook, enforcing module boundaries, setting up CI, using workspaces, and the trade-offs of libraries and applications.
1. Developing at Scale with NxMonoRepos
We're gonna talk about developing at scale with NxMonoRepos. A MonoRepo is a single repository that contains multiple distinct projects with well-defined relationships. A monorepo is great for atomic changes, sharing code easily, and having a single set of dependencies. Code collocation is where you just drop code together without having a monorepo tooling in place. One of the issues is running unnecessary tests, not having code boundaries, and having inconsistent tooling. Nx can help with these problems.
We're gonna talk about developing at scale with NxMonoRepos. So what is a MonoRepo? MonoRepo is a single repository that contains multiple distinct projects with well-defined relationships. So, you know, multiple different apps working together, or you could it could be one app but with multiple like sub projects inside of it. And then you need to have well-defined relationships between them. You can't just if you just dump all the code of two different apps in the same repo. We call this code co-location and it's a mess. And you you'll run into lots of problems with that. And let's talk about what those things are.
So a monorepo is great for lets you have atomic changes, lets you share code easily, and lets you have a single set of dependencies. So let me go into each of these and explain what they are. So atomic changes are, let's say you have an application that consumes a UI library. And if you have that UI library in a separate repo from your application, then the change workflow goes something like this. Say you make a change to the UI library that breaks a test in the application. So you have to publish that UI library, and then at some point later, the application developer bumps up their version of the library and realizes, hey, you broke my application. So then they tell you about it, or they file an issue about it. And then the library developer has to come back and say, okay, I'm gonna fix that bug and then publish a new version of it that will fix the bug. And then later, a few days later, the application developer bumps their version again and says, okay, that fixed it. So that whole life cycle, that's a minimum of a week probably, of actual real developer time. Before the change is made to it's actually fixed and corrected. Whereas if they were the same repo, then the library developer would just run the tests before they even make the commit. They would just run the test and say, wait, I broke that app, I'm gonna fix it. So it goes from a week cycle time to like 30 minutes cycle time and you're not doing that contact switching, going back to this thing you worked on a week ago to finally fix it. That's one benefit of a Monorepo. Second benefit is sharing code. So if you have some user logic to validate whether a username is valid, and you want to reuse that logic across multiple applications or multiple subsections of your app. If you were gonna do that in separate repos, you'd have to publish that and keep the version numbers in sync. Whereas in a monorepo, all you need to do to share that logic is to export a function and then just reuse that function wherever it's needed. So it makes it dead simple to keep that logic in sync. If you wanted to change this logic somehow, you just update that function and everywhere instantly uses the new updated logic. The other benefit is having a single set of dependencies. So, say if you have multiple different versions of your framework of Angular or react, that can cause weird, strange bugs. If you have a library that's on an old version of react and the applications on a newer version of react, there can be like, hard to debug. Hard to debugruntime errors that can be caused by that. The other issue is that whenever you have multiple applications on different, well, you have multiple applications, usually there's one application, that's the main thing that gets worked on all the time. So that's going to be at the latest version of React. Version of the framework, but then if you're going to have another application that's you know you updated every three months or so maybe whenever you get around to it, and then whenever you go to update that, it's always a pain. Because you have to remember, okay, what were the tricky things about upgrading to that version of React that was six months ago, or a year ago. And you have to go through all the same pain points that you did the first time, but 12 months later. Whereas it's a lot easier to do all the application upgrading at the same time. Because you're solving the same problem in 10 different places, instead of solving the same problem in one place, so it's not that hard. Whereas if you do it 10 different times over the course of a year, that's really painful.
Code collocation is where you just drop code together without having a monorepo tooling in place. So one of the issues so you can have running unnecessary tests, not having code boundaries and having inconsistent tooling. So unnecessary tests, let's say you change the products homepage project, and that depends on a UI library. So if you make changes to the products homepage, there's no way you broke the test for the product UI library. So there's no point running those tests, but without your tooling knowing about how this dependency graph, how that dependency actually works. There's no way for your tooling to say, these tests you need to run, these tests you don't need to run. So you need to have something that knows this dependency graph. And so you could theoretically do that yourself. You say I know that this test needs to run, these other tests don't. But a normal repo that has multiple applications, the product graph looks more like this. Or even, I've seen graphs that have thousands of nodes in it like this. And so there's no way you can do that in your head to make sure you get all the tests done correctly every time. So you want to have a tool that can do this for you. So you make sure you run all the tests you need to run, but not any of the tests that are guaranteed not to have broken. The other thing is code boundaries. Let's say you have your code in a shared repo, and you have some function that you're playing around with. It's intended for internal use within your project, and you don't want other people to use it, because it might change frequently, right? But somebody reaches in and starts using that function in their application. And then at some point later, you change it. You've broken their app, and they're upset at you. And so, now you're forever on the hook for keeping that function with the same API, or fixing their code wherever it's using it. So there needs to be some way of saying, these functions are available for you to use. These functions are not available for you to use. So that you can clearly say, here's the boundary, and this is my stuff, internal stuff. This is public stuff. And the other thing is inconsistent tooling. So every application, every project has, you put in NPM script, you have all sorts of weird flags and scripts. And so, whenever you're going into new code base, you have to figure out, what does this mean? And why would I ever run this? And it's really hard to know that in each new application. So you need to have some way of making those things discoverable and well-documented. So this is what Nx can help with.
2. Benefits of NxMonoRepos
It gives you all the benefits of the monorepo without the drawbacks of code collocation. Nx provides linting rules, generators, and plugins for consistent coding practices. It allows controlled code sharing and an accurate architecture diagram. Nx understands the dependencies between projects, ensuring accurate task execution and test running.
It gives you all the benefits of the monorepo without the drawbacks of code collocation. So it can give you faster command execution, gives you controlled code sharing, consistent coding practices, and an accurate architecture diagram. So faster command execution. There are executors, which help you run whatever tasks you need to run against your code, the build, test, and let, those kinds of things. Nx Affected allows you to run commands only on projects that were affected by a code change and not anything that wasn't affected. And Local and Distributed Caching also speed up your average runtime in CI or locally by saying if the inputs for this task haven't changed, then I know the output is already what was done before. And so it just pulls it from the cache instead of rerunning. And Distributed Caching allows you to share that cache across your whole organization instead of just locally on your machine. Controlled Code Sharing, you can set up an API for your projects and say these are the functions that are public for anyone else to use and these are anything that's not exported in the API file, that indexed TS file is private so you can't use it. You can set up tags to say these particular types of projects can depend on these other types of projects, but other ones cannot. And so whatever structure you need to make for your own organization, you can say these types of projects can be used within this team and these other projects are shared for anyone to use. So whatever kind of structure you need to make with those tags, you can do. You can also do publishable libraries to publish to NPM. You use a code owners file, which is like a Git feature which says if you make changes within these folders, these particular people have to approve the PR before it can merge. So all those things are useful for managing a large repo. Consistent coding practices, so Nx provides linting rules, generators let you generate code and modify code. So keep things up to date and add new code in a consistent way. There are narrow provided plugins from Nx which are official, and then there are also community plugins. So anybody can write a plug in and publish it and say, this will give you support for some tool that Nx hasn't supported officially. And then you can have an accurate architecture diagram. So Nx understands how the different projects relate to each other. Not just the way you think they depend on each other or the way you wish they depended on each other or the way they depended on each other six months ago. It's what the code is saying about how these projects depend on each other, which is invaluable for really having an accurate view of which tests actually need to be run and how projects need to, which tasks need to be run before which other tasks those kinds of things.
3. Folder Structure and Creating a New Workspace
This part explains the typical folder structure for Nx, including the apps and libs directories. It also mentions the dist directory for build code, the workspace.json and index.json files, and the tsconfig.base.json file for TypeScript alias paths. The part also mentions workspace level and project level configuration files for TypeScript and Jest. The first lab involves creating a new workspace using npx create-nx-workspace or yarn create-nx-workspace. The name given to the workspace is used for the directory, path alias, and NPM scope. The part concludes with instructions on creating the BG Horde workspace using yarn create-nx-workspace.
All right, so this is a typical folder structure for Nx. Nx, there's different styles of an Nx repo. We're going to be doing the integrated style in this workshop, which is the layout that you're seeing here. The package-based repo setup, it can be basically whatever folder structure you want, and basically what you get the package-based repo is you have less support for the plugins, like the executors and co-generation. Some of that may or may not work as well, but with integrated you get all the core Nx functionality and you get the plugins and generators working the way you expect.
So there is an apps directory, which has all your applications and then a libs directory, which has libraries. So apps will consume library code, and apps, the difference between an app and libraries, is an app has like a way to deploy it basically or a way to, it has like a published feature, it's like an entry point for your code. And where libs are are things that will be consumed by other libraries or by applications. dist is where the build code gets output, tools for scripts or other helper things. Workspace.json we don't use anymore. Index.json is your global code, global configuration file for index itself. And let's see, tsconfig.base.json has TypeScript alias paths so you can reference libraries with a convenient alias. Then we have workspace level configuration and project level configuration. So if you have TypeScript settings in your tsconfig.base.json and you need to overwrite that for a particular project, you can do that at the project level. So tsconfig.json inherits from the base config but then you can overwrite it to change it for whatever specific needs you have for that project. And the same thing for Jest and for other tools.
All right, so this is what we're going to be doing. This is the schedule. And except for replace Angular with React in there. So the first lab, we are going to be creating a new workspace. So we'll be using the, let me, yeah, okay. When you create a new workspace, you run npx create index workspace or yarn create index workspace. And you give it a name and that name is used for three different things. It's used for the directory that it gets put in, it gets used for the path alias. So how you import a library is that if you set my org and you use it simply as the name, it'll be at my org slash the library name. And then it gets used for the NPM scope. If things were published, if you ever publish a library from your repo, it'll let it'll have that, that scope on it. Now all these things can be modified later, but then by default, they're all set to the same thing when you create a workspace. All right, so let me go to the first lab here. So this is the application we're gonna be making. Let's go down to here and go to lab one. All right, so we're gonna make a new workspace and name it BG Horde, and it's gonna be integrated and an apps layout. So let's do that. Let me just quick double check if there are questions. Okay, all right. So if you have questions as we're going, put them in Discord. All right, so let's do yarn create NX workspace, and we'll give it the BorgGame Horde. Actually, I need to remove the BorgGame Horde that's already there. All right, Yarn, create NX workspace B, G, Horde, and let me just do this, let's make sure I have the latest. Oh, that's a NPX thing, okay. Yeah, okay, that's right. Alright, integrated monorepo, we want apps, and we'll do no distributed caching as we'll add that later. Um, okay. I don't think 16 dot 2.0 should be published, I'm gonna try NPX, Clinix Workspace, vg-void. What version is that doing? Okay. This is what I have to do the latest year. Okay. Okay, so let's do 16 dot 1.4. So if you're following along do 16 dot 1.4 instead of 16 dot 2.0. I think that was just published today. So, integrated and apps. And we'll do no to distributed caching. So I think just the create NX workspace was created for 16 dot 2.0 and the rest of NX was not published for 16 dot 2.0, cause it's still in beta. So I'm not sure why creating NX workspace was created. So if you specify specifically 16.1.4 I'm not sure exactly how to specify a version in Yarn Create. Does anyone know how to do that, do like a dash dash version? Here it is dash V. No, that's just outputs it. Try to put a it in a number version after your thing. Yeah, I tried that and it's. And it got confused. With binary message file or directory. By the way. It works for me, so we're going to go in here. And this is our new repo. So if I want to use Yarn instead, I can delete the package log and do. So there we go. So let me know if you're having issues, or if you're following along. So you should have NX 16.1.4 in here and NX Workspace with the same version. It's important that these numbers stay in sync with each other. Otherwise, they'll be looking for functions or values that aren't there.
4. Using Plugins and Generating a Store Application
We've discussed plugins and how they can help with code consistency and automation. We've added the NX React plugin and other packages. We've used the NX console to generate a store application with React and webpack. We've created a fake API.ts file in the store folder.
OK, so we've got no apps and no libraries, and just some basic stuff here. All right, now let's move on.
All right, lab two, we talked about plugins. So if you run NX list, it'll list out all the plugins you have installed, and then also list out other plugins that you can add. To add a plugin, you just install it with npm or yarn, and then you can use it. Plugins have generators and executors, and a few other things that interact with the NX Graph, kind of extend the NX Graph. But most of them are doing generators and executors. So generators update your code, or modify your code in some way. Whereas, people writing on the screen. Whereas, executors run tasks on your code in some way. So you can run generators from the command line with nxgenerate. Or you can use the NX console editor plugin. So NX console is available for VS code and for the IntelliJ products Webstorm and others. And that will give you a UI that show you all the flags that are available for a particular generator and allow you to run things that way. NX provides official plugins that give you support for different frameworks, AngularReact, Storybook. And testing tools like Jest and Protractor and Cypress and linting with Prettier and ESLint and other back end tools as well. You can also make your own local generators, make your own local plugin that's just intended for use within the repo. And that allows you to make basically an automated way of repeating tasks that you do on a regular basis. So every time you make a new page, it's usually like an eight step process that's in a read me somewhere. And people always forget step three and seven. And so if you write a generator for that, then you can make sure that it's done consistently. And all they do is they type in the name of the new page, and it just fills it out for them. So that's a great way to increase consistency across your codebase. All right, so let's do lab two. I'm going to make a React app here. Let me pull up the instructions. Lab two. Going to make a React app. And so we'll do Nx version, make sure we have Nx dash dash version here. So we got 16.1.4, which is what we expect. And we'll do Nx list to see what we have. So installed plugins just Nx, which is what we expect. And we have all of these official plugins available to be added. Now there are community plugins as well. We can go look at this link to see them. So you got all these, these are the official ones. And if you scroll down here, you can see that there's a bunch of community plugins. So if I search here, I'll search at the top. Let's look for a Java one. So Java. Okay. There are, maybe, it's called Spring. There we go. There's a Spring one, which is Java. So anyway, you can look on there for other plugins. Okay, so we're gonna add NX React. And then we'll also add some other packages that we'll need later. NX React and Material UI to add some styling. Okay, add those. And also 16.1.4, which is what we expect. This should actually probably be a dev dependency, but it doesn't matter that much. Okay, so now we need to use this React plugin to generate a store application. So here's what we're gonna do. I could do nx generate at nx react application like that, and then it's gonna ask me what name I want, but I'm gonna use the NX console here, and I'm gonna say I wanna generate, and it found the React library, a React plugin, and it shows me all the generators that are available, so I'm gonna do application here, and make the name of it be Store. It's fine, and I can choose a bundler, we're gonna use webpack, because that's what the workshop is using, but on your own you can use VEET or RSPack, and then you can specify a directory you wanna put it under if you want to. There are more options if you click under show more, a bunch of stuff that goes in here, but we'll just leave that, and run it. There we go. So the first time you make an application, it has to install all the things that are needed, so it's going to add, wait for it to finish, but eventually it's gonna update your package.json with the React framework that you need, and yeah, here we go. All the libraries that are needed for React, ESLint, Jest, that comes default, and some other things. And here, NX console knows that I have the Store application now and a Store end-to-end application. You can see them underneath apps here. So we've got Store and Store end-to-end. All right, so let that run and we'll do the next step in here. So we're gonna make a fake API.ts file. And let's go and make a new application C2 copy, copy. Fake API, I wanna put it any source in the store folder. And here, I'm gonna make a new file, fake API.ts. Paste this in here. Like that, okay, that's right. Okay, we've done that.
5. Updating TypeScript and CSS
We're gonna update the TypeScript and CSS. We modify app.tsx and the app modules SCSS. We copy the example images and serve the store to see the styling, material cards, and images. We have a basic setup with React, testing, and linting. Running the tests and NX Lint will work. We can also run N2N tests with NXETE store ETE.
Step seven, we're gonna update the TypeScript and CSS. So, app.tsx gets modified with this. Our default app.tsx is using the, some introductory welcome code in here that tells you, hey, this was made by NX and gives you some next steps, points you to some tutorials. And then this is the app modules SCSS. Which we actually need to rename here. This is pointing to SCSS, not CSS. Although it actually wouldn't matter if this was CSS.
Okay. We've got that. Okay. And then we can copy the example images. I'm actually going to copy them from a different folder, but you can download them. See, it's... See, it's complete. I'm going to drop the images into here. So if you're doing this yourself, you download each of these three and paste them in. But I already have them locally. So I drag them over. Okay, so let's serve this store and see what it looks like. NxServeStore, so we can do this with the CLI like this, or we can also use the NxConsole here. We expand this, you can click on serve, run it like this, or you can click on serve here and choose the application you want. And yeah, we click on that and run it. So it's available in localhost 4200, and here we go. So we've got some styling, material cards, and some images being shown. Great. So that's pretty nice. We, without much effort, we had a, you know, basic setup with React, and we have, you know, testing and linting already set up for us. If we run the tests, it'll fail because we changed the code, but the infrastructure is there for us. And we can do NX Lint, store, that'll work. And we have N2N tests. You can run NXETE store ETE. And that will launch the application and run Cypress test against it, which these will also fail because I've changed the application code. But if the infrastructure is in place there for you to modify those things.
6. Using Executors and Configuring Tasks
Let's talk about executors and how they give you discoverable flags to run commands. We'll explore building the application and configuring the executor for production or development. The serve target uses the build target, allowing you to change options in one place. Executors are fully configurable.
All right. Let's go on to the next slides. Let me double check Discord again. Next. Yes. So in, in yeah, well you have to specify when you create the next workspace, you have to specify at 16.1.4. Yep. Mayo specified that. Yeah. 16.2 is the issue is that 16.2 is looking for NX version 16.2, which hasn't been, has not been published yet. So the Create NX Workspace 16.2 should not have been published, but it was. So that's, that's why. So if you just follow a Males, copy Males, command there it should work. All right, let's go back to Keynote here. All right. We just did lab two. So now let's talk about executors. We use the generator to make the application, and I will talk about executors. Executors are defined in your Project.JSON files. And if you have something defined in npm scripts using like the package-based setup, you don't have executors. But you can still run your npm scripts using NX. We're just buying, you know, NX. If you have a build script in your npm script, you do nx build, whatever the name of your project is. But executors give you a... You run them using, you know, this command line. You can also run them with NX Console. Same thing with npm scripts. But what the executors give you is they give you, the flags are discoverable. So with npm scripts, you would not have these fields laid out here in NX Console for you. You also wouldn't have, they'd be discoverable in terms of like what their name is and you could run them, but you wouldn't be able to say, let me add this other flag and have this line here telling you what each flag does. So that's what executors give you. So let's go to the next lab, and we're going to, explore what they do for you.
All right, we're going to build the application. So we just did a serve here, let me commit this, this is what lab two. Let's look at project, well, we'll build it, NX build the store. Nope, not store, eStore. Okay, so built it, and if we look in the disk folder, we have the build output here. So what if we want, it would all work, if we look in the main.js, you've got this and all that gibberish. So let's look and in the project.json file. So if we look under build, it's using the webpack executor, and we have output path, we have base href, bunch of things in here, some assets that are getting copied over. So this is how we see that the images that get created, this is how you define where they're coming from. So it's copying this whole folder. All right, we wanna send a flag to the executor so that it builds for production. So let's do this. So there's two different configurations here. So these configurations, they start with these options up here and then they overwrite them in a specific way. So development has these things set to false, these to true and production has some file replacements that are happening and then some other options that are overwriting these options. So if you, let's see, if I run this, so default configuration option is production. So if we, if you do NxBuild-store "-configuration="production", that's the same thing as what we already did. But you could also do development. And that has some different settings in here. So if we look at the, so now your main.js doesn't have the minification that was happening with the production build. Yep. So we got this, look a little bit lower. The serve target, we can look in here and we can see that let's collapse this build. The serve target uses the build target. So serve says, hey, use all the same options that are in the build, but just, serve it instead of building it. So that way you only have one place to change these options here. So when you're in development mode, when you're in development mode, you always use this, whether you're building or serving. Yeah, cool. So that's a quick intro to the executors.
Yeah. I have a question about the task like serve, a link, something like that. It's fully configurable. We choose whatever you want? Yeah, the tasks. There, yeah, so in here, so these names are totally arbitrary. You can put whatever you want in there. These executors need to be a specific executor that's available.
7. Understanding Executors and Configuration Options
These executors need to be a specific executor that's available. Each executor has its own options that are available. NxConsole enables autocomplete for options based on the executor's metadata. Outputs and inputs can be specified, and default configurations can be set. Configuration names are arbitrary. Lab three is complete.
You can put whatever you want in there. These executors need to be a specific executor that's available. So this part is the plugin name. And then this is the name of the executor. And then each executor has its own options that are available in here. So if you have NxConsole, it should have an autocomplete. There we go, yeah. So if you have NxConsole enabled, it will autocomplete the options that are available in here by reading the metadata that's provided by this executor. So these options will change based on whatever this executor is. And then these configurations, the options that are available in here, the same ones that are available in the options up above.
Does that answer your question? Yeah, thank you. Yeah. And then there are some other options out here that are not specific to the executor. So outputs, this comes into play when you're doing caching. And then there's also, you can also specify inputs. I think we'll get to this a little bit later. And then default configuration, this is when you don't specify configuration, what it's going to use. If you want to make this be development, you can do that. Or if you, same thing with targets. The configuration names are totally arbitrary. You could call this staging or whatever you want to do. There. So some people typing. Let's move on. That was lab three. So we will have another another half hour before we have our break.
8. Libraries, Code Organization, and Migrations
Libraries can be categorized into feature libraries, UI libraries, data access libraries, and utility libraries. They have specific dependencies and can be nested under folders. Splitting code into libraries is a trade-off, balancing the benefits of code organization and the cost of generating more folders. Visualizing the project graph helps understand the repo structure. NxGenerators can be used for migrations to keep code in sync and update to the latest version. Running NxMigrate updates code and automatically migrates breaking changes introduced by plugins.
And typically, you have feature libraries that can use any of the other libraries. UI libraries can only import other UI libraries or util libraries. And data access libraries can only use other data access libraries or util libraries. And then, util libraries can only import other util libraries. So you have this kind of structure in place for how you set up things. Libraries can also be nested under folders. So typically, if you have a store application, you'd have all the libraries that are intended to be just for that application inside a store folder inside of libs. And then, libraries that are intended to be shared across the whole repo underneath a shared folder. But you can move libraries around easily. So you don't have to sweat too much about where you put them initially. We have a generator to move things around. And you can have your own structure, of course.
So people often ask us the question of, when should I split code into a new library? And when do I have too many libraries? Like anything, this is a trade-off. Every operation that NX does is at the library or project level. So the more you split code up into multiple projects, the more you can take advantage of NX's caching or NX's code generation, things like that, and the effective command, those kinds of things. So if you had everything all inside of one application project, then there's not a lot of point in using NX. There's not a lot of use to that. But then, on the other hand, if you put every single component into its own library, that's probably too much, because there's a cost to that. You're generating more folders, and then whenever you make changes to code, you have to look in lots of different folders to understand what's going on. So just like anything else, you want to keep code together that makes sense together, but when it starts to get bigger, then that's when you split things out into multiple libraries. So you can visualize your project graph so you can see what the structure is of your repo. This is the NX repo itself. This is a video here. Curious with this decision. Okay, so you can group things, you can filter things down and make sure that you're only showing specific parts of the graph. So with any reasonable size repo, you'll get hundreds of nodes. And then at that point, a static rendering of the graph is completely useless. So you need to have some kind of filtering like this in order to actually use the graph yourself to get any value out of it. So, there's that.
And then I think, okay, so we're moving on to lab four. Actually, a quick detour here. Actually, a quick detour here, lab 3.1, migrations. So migrations. NxGenerators are not just for generating new code. You can also use them to keep people, as a library author, you can use them to keep people's code in sync. Whenever you make a breaking change, you can put out a migration that will that will keep people up to date with the latest version of your library. So NX does this for all of our provided plugins. So you can run NxMigrate, and that will update all of your code to the latest version of Nx. Then you could run the migration scripts themselves. Then any breaking changes that were put in place by our plugins will be automatically migrated for you. So it makes it very easy to stay up-to-date to the latest version of things. So we're using the same tool to jump to different parts of the workshop. Different labs in the workshop. So if you ever get behind, you can install this NARAL nxreact-workshop package, and then you can run the migrations here. So I'm going to just demo that real quick. So I'm going to do yarn add-d to workshop. And then I'm going to run. Well, this is suggesting we add the oldest one, but I just added the latest one. So if there was, if this was, if I did add an old one, I could run xmigrate, I'll just run xmigrate. That'll do everything in your package JSON. xmigrate. You can do xmigrate latest. That'll just do everything. Oh, no. Well, OK. Well, I guess you'll see what this does. So if you do an xmigrate latest, it does everything. So apparently, during our workshop in the last hour, they published 16.2.1, which is probably why that CreateNX workspace was doing weird things. And so you'll get to see what happens when we update. So it created some migration scripts in this migration.json file. Remove output path for run commands. It normalizes, does some things with Cypress, and it's fixed the React test renderer. OK, so those things, sure.
9. Running Generators and Migration Scripts
To run the generators and migration scripts, ensure that the latest packages are installed. Running nxmigrate run migrations checks the migrations.json file and executes the generators. It's important to have a clean git history when running generators or migration scripts. After running the migrations, you can delete the migrations file. The migrations file is useful in larger repositories where some generator scripts may not work or when merging PRs from multiple developers. The complete labs generator can be used to complete a single lab or multiple labs. The NxGenerate app can be used to create a new React library with the nx rollup executor.
10. Creating Header Component and Running Labs
We created a new react component called header inside the libraries folder. We ran the migration to copy the code over and used the header in the app.tsx file. We refreshed the page and saw the header. We looked at the project graph to see the dependencies. We ran lab five to seven, which created more libraries and an Express API application. Lab five introduced a utility library using NxJS instead of NxReact. Lab six introduced a routing library using NxReact. Lab seven installed the NxExpress plugin and created an application with it.
11. Connecting API and Store
The API was created using an NxJsNode with an Express app and routes. The fake API.ts file was moved to the server side. The header and formatter library are used, with the format rating being defined inside util formatters. Lab 8 will connect the API and the front-end store application. Circular dependencies should be avoided, but can be disabled if necessary. Lab 8 involves deleting the fake API, using Fetch and Use Effect in FF86 component, and serving the API and store.
So let me go through each of these. So the API here was created. It's using an NxJsNode to run it. And this is an Express app with a main.ts file that has some routes in here. And we copied over the fake API.ts file has been moved over here to the server side. But this is still the same code basically. That's what's going on in here.
Here we've got that same header that we created before. Oops, I didn't want to do that. This is using the util, the formatter library here. And this is the route that was autogenerated for us. And this is the route that was autogenerated for us. And then here inside of util formatters is the. So here, format rating here. That's where it's being used. And then format rating is defined inside of util formatters. And then feature game detail is a route where you can click on, get more details about a specific game. So let me just run this. I'm going to serve the store here. Refresh this. OK, so the only difference you can see is the rating doesn't have that long decimal anymore. And if you click on one of these, it shows some details and the URL is changing. In our next lab, we're going to actually connect up this API. So in our next lab, in lab 8, we're going to actually connect up the API and the front end store application and we'll show how that works. This is actually a little bit more complex than just generating code and copying it over. So yeah, Perkup is asking about circular dependencies within NX. Yeah, generally speaking, you don't want them. It is possible. So there is a lint rule that blocks them, but you can disable that lint rule if you need to. But generally speaking, if you have circular dependencies, like two libraries that both depend on each other, you probably want to move some code around so you don't have that case. So generally, you want to either just combine them into the same library or pulls some code out of library 2 and put it in library 1, something like that.
So lab 8 is hooking up the front-end app to the back-end app using real network calls. And so let's do that. So we move the fake API out of the store application. Let me commit this. It's 5, 3, 7. Sorry. I always have to Google how to kill a process on a port. There we go. That's what I want. Burned. Isn't that good? So that's there. So now we are starting on lab 8. So delete the fake API from the store app. I think that's already done. It's not done. So delete that. Use Fetch and Use Effect in FF86 component. So go. Copy that. And let's see. Where's the Fetch? Where's the Fetch? I think. Just save that. OK. And then it's that. I think that's it. OK, then we'll serve API and serve the store. OK, I need to get rid of these warnings here. Serve API is on 3,000. Add a new terminal, NX serve store. The store is on 4,200 and the APIs on 3,000. Now, if we go back here, let's see if it works. Refresh. Some CSS is doing weird things, but we've got a network tab here. And refresh. I'm still playing. There we go. That's what I want. I think I've got a setting to, yeah, I don't want to preserve the log. That's what I want.
12. Updating Files and Connecting APIs
13. Creating Type Library and Using in API and Store
Then Ben's question. We have an NX project containing many libs and apps. Override the Google specific lib, pre-commit one if files change for a specific lib for a commit. I am not... I know I use Husky. I have not written a lot of pre-commit hooks myself. Let me try to think, if files change for a specific lib. Uh, yeah. Yeah, so, basically what you would do Ben is, in your pre-commit hook, you'd run the NX affected command with whatever target you want it to be. And it would run that task, that target for all the libraries that were affected by the change. So yeah. Yeah, you could definitely do that. Yeah.
Okay, so let me go back. I think I've answered those questions. And let me go back to where we were. Okay. So, our goal is now in lab nine, starting lab nine. We're going to create a new type library and use it in both the API and the store. All right, so we're gonna make a util interface library inside of the API folder. There. Let's just paste this. Okay, nx generate library and we'll call it util interface and we'll put it in the API folder. We'll make it a JS library. That's fine. That's fine. All right, so underneath the API folder, we have util interface and API util interface is here and copy this code. Okay. So it's a game interface for us and we're gonna import it in the gamesrepository.ts file. So basically, this is going to be an array of games is what we want. And important API service, let's build the API NxBuildAPI, make sure nothing broke. If you notice the Nx is when I did the build API, it did the build on dependent project tasks. So what that was is if you look at NxGraph again, when I build this API, it saw that I have a util interface that also has a build command and says I'm gonna run build on this before I run build on this. You can also see this visualized by task and show all the build tasks here. Yeah, so API build depends on API util interface build. So you can see this will always be executed before you run that. We'll see how this works. Okay, so that's we're there and run it, inspect the dependency graph, good. They want us to commit everything, okay, we'll commit it. This is lab nine, part one. Okay, so let's use it in the front end now. So we have this data ne here and app.tsx. So let's fix that. We use the actual, the actual game interface here. Why is it not, hello. Comporch, that doesn't look good. Okay, I will fix that. Okay, so you fix that by putting the game in here. But this looks weird here. We don't want to be importing from the API folder within the store application. This doesn't look right. So let's move this, this util interface, out of slash API and put it into the top level. We could also put it under shared, but for this workshop we'll just put it in the top level. So we'll use the move generator to move that library into a new location. We'll do this. Generate, move. You can also, if you don't want to click there, you can do it completely from the command line. You can do generate, and X generate, and search for move. And my fingers are not leaving the keyboard at all. Move, and we're going to go, the project name, we want to pick.
14. Moving API Util Interface and Generators
We moved the API util interface to just util interface. The code and files were updated accordingly. The dependency graph shows that the util interface is being used by the store, the feature game detail, and the API. If different languages are used, manual edges can be added to establish a connection between projects. The next lab will demonstrate the power of generators by generating storybook stories for components.
This one doesn't have auto complete. I don't like that. Well, API, util interface is the name of the library. And then we're going to move it to just util interface. To move it out of the API folder, and up to util interface. And let's try that. Execute it. So let's see what happened. So it's got moved up to libs util interface. That looks right. So it used to be under a slash API util interface, and now it's just util interface. So that looks right. Now let's look at our games repository here. This code got updated, so this is correct now, and the app.tsx file here, this code also got updated. So everywhere it was used also got updated. So it's really, you know, handy. Wherever things have been used, it'll be automatically updated. And also your tsconfig, this obviously got updated, too. So it was instead of slash API, slash user interface, it's now just slash user interface. Okay. Remove that, import. Oh, this is badly formatted, sorry. But I can, at the TSX, I already did that. And in the feature game detail, I want to update that too. And data in a, this should be data, I think. Yeah, I don't know why this. Here, I'm going to update TypeScript, restart the TypeScript server. Sometimes TypeScript server needs some, some help. There we go. Data. Oh, and I think it is a partial game. Let me look and see what was supposed to be. Yes. Partial game. All right. Now it's helping me to get the signal down to what I want, so that's lucky. So it helped me. Okay. Now it's happy with this empty object here. Okay. That's working there, so then we can, uh, build both the API in the store, check the dependency graph. Next build store. Okay. That worked in next build API should still work. Okay, great. And let's look at the graph. And there we go. So util interface is being used by the store, the, the feature game detail and the API. So anytime this utility interface changes all three of these places It's going to be updated. Great. So obviously if you're using different, different languages, you're not going to be able to share types like that. But you can, in that case, what I would do is I would add a, a manual edge here. And the way you would do that is you, I guess you'd probably do probably do from the front end to the back end. The store project JSON here, and you'd add a, an implicit dependency on the API. And then if you look at the look at the graph again, Fresh it. Yeah, so then store has an implicit dependency on API. So you could do it that way if you had a an API that was written in different language. And you wanted to make sure that there was a connection between the two. Sometimes you can get away without having that connection at all. But it's up to you in how you want to structure things. All right, questions from this part. I think that's OK. No new questions in this chord. All right, I'm going to commit that. Lab 9. lab 9. And the next lab is a shorter lab. But it's just showing the power of generators and the kinds of things you could do with generators if you're a little clever with the way you're using them. And we're going to generate some storybook stories for components. Oh, this is going to get, hold on.
15. Using the Storybook Configuration Generator
So when using the storybook configuration generator, make sure the versions are in sync. Use the UI shared project and generate the storybook configuration for store UI shared. Check the checkboxes and run the generator script. It will create files and a stories file. Set up an input for the generator to automatically create knobs in the story. Run the generator script again to recognize the input and create a knob. Install dependencies and serve storybook. Check the default configuration and make any necessary adjustments.
So when you do this, make sure the versions here are in sync. Yeah, that should be 16.14, not 16.21. So they just published 16.21 while our workshop was going on. So that's why this is out of sync. So if you were, if you're going to run this, make sure you do at 16.14 in there.
So we're going to use the storybook configuration generator to create some stories. So set this up, who do generate, which, which project do we do it for? The UI shared project. Generate and the storybook. Graph storybook configuration. That's what we want. And then we want to do it for store UI shared. And this is just the dry run here. And we're going to check all these checkboxes or just say yes to all these questions and run it. And this creates these files, update some. Some things it created a stories. Created a stories file inside of the. The header component or next to the header component. Let's. Get to that later. Story, you shared here here. OK. Stories. And. Oh, that's the other thing. That's one thing I didn't do. I didn't set up. A an input for this, so it the the generator would find inputs and automatically create knobs for you in the story, but because I had no input set up. It didn't. It didn't do that. So let me. Let me just do this real quick. Title string actually undo what I just did. Yeah, I can just do all this. I'm going to undo that and then I'll redo it later. To add a title, and that's string. And. And let's make that optional. Perhaps that title or. Do that. And that should be it. Okay. Do that, and then now we're gonna run that generator script again. Okay, here we go. So recognize that there was an input there, a prop being passed in. And so it created a knob for you in storybook. And zip it up. So let's look and see once it finishes installing dependencies. We're gonna serve storybook. Okay, looks like that finished. It's good. So inside of here, it added a new target. Test, here we go. Next storybook. So we'll call NX storybook, store UI shared. What's going on? Oh, that's using beat as the default. I'm gonna have to roll back again, I think. Okay. So I'm gonna expand this and I think the default is Vite now. Nope, where is it? There should be a setting on here. Yes, TypeScript base configuration. Hm. All right, let's look at this. That's using that. It sets up a dot storybook. Yeah. Let's see. This needs to pre-reactWebpack, I think. As we have it, we have it set up with Webpack. Or, oh, I know what it is. I think it's that this is not installed.
16. Configuring Storybook and Setting Project Rules
There was an issue with the configuration of Storybook, which was resolved by installing a missing package. Storybook was set up to render a component with default options, and there is a way to dynamically change the input using ARGs. Cypress tests can be set up to run against Storybook, and the generator can generate Cypress specs for each story. Tags can be added to projects to define dependencies between them, and rules can be set to control how projects with specific tags can depend on each other.
That's the issue. No build or configure, yeah. Packages JSON at storybook slash React feed. Oh, no, it's there. I just haven't run the arm maybe. Nope, not there. Nope. Does this have? Yeah, there we go. All right, so I'm going to, I'm not sure what the issue with that is, but I'm going to try switching this to just React. Okay, it doesn't like that. Oh, okay. So that should be there. And this should be here. In that find module beat, okay. Okay. I think that's the issue. I think we need to install beat. Yarn, and, yeah, OK, there you go. That's better. Okay, so here's Storybook. And we got the header with some, I'd say there's no options here. Well, so it's rendering our, rendering our component here with these default options, which is board game Hoard. And it's been a while since I worked with Storybook, but there is a way to set this up to have a, like a text box that you can fill in and you change the, change the input dynamically, the prop dynamically. So ARGs. Okay, well, I'm not going to do this live. But this sets up Storybook for you and you can also set up a, I wonder if you can do title equals, okay. It doesn't like that. Oh, that question mark, it should be an ampersand. There's some setting that I'm missing here to set up the knob for title. We will just pause that. Then you can also set up Cypress tests that run against Storybook. That's the next lab, which we don't have to walk through. But in that generator, those checkboxes that I ran through. This one. This checkbox and this checkbox generated Cypress specs for you for each story. This story UI shared ETE test is an ETE test, but it's an ETE test just for the component. It's testing it the way a user would, like through Storybook, but it's just that isolated component. It's running Cypress against that Storybook instance and clicking through it and doing whatever actions you want to do just on that specific component itself. I'm going to module boundaries. Each project has a tags property here. Say you have a project of repo that's set up like this. You got two applications at the top and depending on libraries inside of them, they can be Angular or React, whatever framework you're using. You want to have some rules that say that that's sales library is not allowed to depend on the games library. Because games is specific to store and admin code shouldn't be using it. You can add tags to things. Tags are just strings. Any string you want them to be. The convention is to do like a kind of a category first and then a colon and then the specific value for that category. So we have type app and type feature, type util. And then you can have a scope. You can say scope is kind of like the so type is what kind of thing it is. And scope is like the app it belongs to, basically. So scope store for those two things and then scope admin for those three projects over there. And then scope shared for the things at the bottom that everything can use. Or, yeah, scope core. Scope core and scope shared are. Essentially the same thing, so I don't. I don't know why you would use scope core instead of just using scope shared for everything, but that's fine. But so then once you take everything, you can set rules for how these how projects with these tags can depend on each other. So you can say anything with the tag of type app is only allowed to depend on type feature and type util. So then a type app cannot depend on the type app, right? You can set this other rule. Type util can only depend on type util so that blocks you from a type util, depending on type feature. You can set up a rule to have scope store only depend on either scope store or scope shared or scope core, and that stops you from having a scope store depend on the scope admin. So then with this setup that that puts some some structure around these things and it it allows you to stop that that sales project from depending on the games project because it scope admin should not depend on scope store. It should only depend on scope shared or other scope admins. Right, that's what this rule is saying. Here, scope admin can cannot depend on scope store. So that blocks that. Yep, so let's um. Let's do that.
17. Modifying Enforce Module Boundaries
We've applied tags to all nine projects. Now, let's modify the enforce module boundaries in the eslint file to add rules. By default, there is a rule that allows anything to depend on anything. We can run lint on multiple targets using nx run many. We have a rule that restricts scope store to only depend on scope store and scope shared. However, API is currently depending on util interface, which violates the rule.
So project.json this tags property is where you define what the tags are. And so let's let's add these tags to. Scope store and scope type app. So that's the store project. Project. OK, so we're just going to go through every. Every project json file like that. And update the tags. Here's the tags. So this is scope store and type app. Right, a pi. Tags are down here. This is not scope store, this is scope API, but it's also type app. And it's the other project jsons. OK, this is. The tags are appear now. So this is you, I share, so this is scope shared. Actually, no, this is scope store. So it's it's it's underneath the. It's underneath the store folder, even though it has shared in the name. It's it's really scope store and this is type UI. Project JSON so e e. OK, there's no tags in here, so we have to edit tags. We'll give this a scope of ETE I guess. And. I don't type. ETE Now I guess maybe this would be scope API and type ED. This is the API ETE. Say that and then. Yes thanks. This is scope store and type ETE. Project JSON. Util interface. Util interface is scope shared. And type util. And type util. This is scope store. And type ETE. ete. Util formatters. Yeah, this is scope store. And type util. And one more project to go. Store feature game. So scope store and type feature. All right, so now we've applied tags to everything. All nine projects in here. 1, 2, 3, 4, 5, 6, 7, 8, 9. Yeah, now we go back to the instructions. And we'll look in the eslint file. And we're going to modify the enforce module boundaries to add some rules to it. And so I'm going to just copy this as a starting point. All right, enforce module boundaries. Cool. So by default, it has this rule in here, which says anything can depend on anything. If you were to remove this rule, here, let me just run lint the way it is. You can do nx run many to run multiple targets at the same time. So nx run many with a target of lint, and then you can specify a list of projects by name, or you can just, if you don't specify projects, it'll do everything. So this is running lint on everything. It all passed. If I were to remove this, then nothing would pass. Oh, no? OK, I guess that just says it's fine. So nothing is the same thing as that default rule. So here we have this rule saying scope store can only depend on scope store and scope shared. Let me just run Lint real quick here. OK, here we go. So we already have a problem with API. So API is depending on this util interface. So the issue here is that we haven't fully listed out. Anything that doesn't match one of these rules is going to error out.
18. Setting Up Dependency Rules and Caching
We've set up rules for scope and type dependencies to ensure proper code organization and prevent unauthorized imports. Running lint confirms that the rules are being enforced. We also tested failing scenarios to ensure that the rules catch any violations. This automated process is especially valuable in larger repositories where manual reviews become impractical. Additionally, we discussed the caching feature in NXJSON, which improves the performance of linting operations. If you have any questions or specific topics you'd like me to cover, please let me know. If time permits, we can also discuss CI.
So anything that's not scope store that depends on something is going to say, hey, it didn't match one of these rules, and so it broke. So we have to fully flesh out all of these rules. So we can add scope API. Depends on that, or scope shared. And then third one is scope shared. Can only depend on scope shared. OK. So that sets up that. Those are all the scope rules. And now, if we run lint, everything should pass. Oh, I've got a comma here, maybe. Yeah, that's a list. OK. So everything is passing now, but these are just the rules for scope. So we're going to do the same thing for rules for type. So we have type. Let's do type feature, is that what I called it? No, let me feature game detail. Typed feature, yes. Can only depend on, so it can depend on other feature libraries. It can depend on type util. Can depend on type UI. And then we could also add in type data access, but we don't have any of those right now. And then we can do type UI, not allowed to depend on feature, but util is fine and other UI is fine. So let's get that. And then type util can only depend on type util. Let's copy this one and do the same thing for app. So type app. So if I were to remove this and not have it there, then it would just say app could depend on anything. But we don't want apps to import other apps, which is actually apps don't have an alias made for them. So they can't depend on each other anyway. But yeah, this makes it explicit, I guess. Yeah, I think that's everything. That's a really fast comma. And let me just double check, finish heading those rules, and Nx run many. Let's run those levels again. OK, so that all passed. Now let's see, let's make them fail. So we'll copy this. And in the header file, we're going to import from the feature game detail. So it should break the this one. Type UI should not be able to depend on type feature. So we're going to just add an import statement up here. So it's already complaining. So there we go, so the lit rule is already failing inline in the editor, because project tagged with type UI can't depend on util or UI. And if we run lint, and if this got pushed up to CI somehow, it would fail the lint step, because this would fail. So that handles that. And then we could also double check some of the other rules if we want. If we want boundaries between scopes, so stop the API from importing the format rating from format util. So we'll go over to main.tsx, main.ts and import here. Same thing. But scope API can only depend on scope API and shared. It can't depend on scope store. So not allowed. You can't do that. So this is nice. In a smaller repo, you could take care of this just by doing PRs and stuff or doing pull PR reviews. But in a much larger repo, that becomes untenable and you can't maintain it. So having a computer do that for you is great. So if I run an excellent again, it all passes. It all passes super quickly because everything's cached. All right. Questions? Other things you want me to cover. So it's cached because in NXJSON, we specified lint as a cachable operation. And so by default, it will cache. All right. There's four of you left. We can do very fine-tuned, specific to what you care about. Questions now? Yeah. It's been very interesting. I think if we have time, I will listen about CI.
19. Setting Up CI with Effective Command
Let's set up CI using the effective command to run tests only on the affected code. The CI.yaml file is configured to run tests for everything affected by the PR. Effective allows running commands on specific changes. The fetch depth of zero ensures checking out the base commit as well. This way, NX can compare the filesets.
Yeah. But I don't know about the others. Yeah, we can do it. So CI, do we dig into setting up caching locally first, or do you want to just go straight into setting up the CI using the effective command in CI and doing that? Yeah. Why do you think it's more overall? Because I'm just starting with NX. And I want to use it on one of my projects, which is getting pretty big. So I think that's a good enough idea for a project. So it looks very useful to get more an architecture thing. Because I had it when I used the Angular, but now that's on this React project, I'm missing all this scope. So we have a more controlled environment now. Yeah. All right. So let's do. So just so you're aware, this content is all here. It's all these. Let's see. So setting up today, I see lab 15 here. So we can dig into that. The other thing that's also helpful, workspace and not workspace generators but local generators. That's very helpful in large environment to set up automatically creating things and modifying the generators that are provided to you to use the flags that you always use in your repo. But we'll skip that. All right, so let's do big into setting up CI then. All right, so to do this, we first need to, I need to push this up to, I'm 13, I think, I don't know. No, I think that was 12, but it doesn't matter. I need to push this up to a repo on GitHub. Okay, so here we go. This is a setup. Let me actually, this is a previous workshop that I worked on, so I'm just gonna pull this down cause that'll be faster. I don't want code spaces. Local, that's better. All right, I'm going to just remove that. Let's split this. Then check up the other one. Okay, that's, that's. So this is, it's got the same API. Oh, this is, so this was using a node setup instead of react, but that's, shouldn't matter for CI. Okay, so this, I've gotta CI.yaml here. All right, so I've got a CI.yaml here and it's set up with the, so initially, you'd set up your CI.yaml with something like this potentially where you have, you test one thing and then you test the other thing, but the more efficient way of doing that is doing, instead of explicitly testing API and explicitly testing store, it's just to say, let's run test for everything that was affected by the PR. And there's a change I need to make here to the CI here. GitHub actions. Next one, I check. This is what I want. That is what I want to do this. Steps checkout, yes it's doing that. Figure this out. Steps checkout, yeah this is what I want. Uses that and then runs the VPCI. Okay, so this, so what Effective does is, I guess I haven't demonstrated this have I? Let me let me stash this, stash that. Let's say I make a change here to this API. It can be a real change or just a comment change and then I do nx affected dash dash target equals test. What's going on? Oh, I forgot to install it. Okay, so this will run test just on that API cause it can tell I change this and there's nothing that depends on it. If I look at the NX graph. So this is a slightly different repo. So let me show you what the graph looks like here. API has only the ETE tests that depend on it and nothing else. So I made this change here and nothing else can be affected by it. If I change the API auth, it should run tests on this, on API auth and API. So if I go in here, and libs, API auth, let me make a change here. And I could even delete this change here. And now I run NxEffected. Now it runs on API auth and API. The tests failed, but we don't care about that. Yes. Yeah, okay. Yeah, so that's what Effected does. It can see what was changed and run the commands just on those things. So now, if I were to, I'm gonna git-pop these changes here. So in the CI here, I have it set up to check out our code and with this fetch depth of zero, cause I want to make sure that we also check out the, we can't just check out the commit that is being tested. We also have to check out the base commit, the commit before it, because that will, NX needs to compare the two different filesets there.
20. Setting Up CI with Effective Command
The basics of CI involve running tests on the specific projects that have been changed, rather than running tests on the entire repository. If you want to use executors, you need to switch to an integrated repo with a project.json file. However, if you have no need for executors, you can continue using a package-based repo. All caching and task pipeline setups will still work. You can set up dependencies between builds and tasks just fine. If you have any specific questions or need advice for your repo, feel free to ask.
So that's, otherwise the NX effective command won't work because it has nothing to compare against. So let me push that up. Push that up and I'm going to go look at, actually what's on main. Okay. Let's go here. And let's go look at. Okay I'm going to make a new PR here. And let's do this. Let's do, let's do on our API auth here. Call it changes made. And let's give it a new branch. And then push the PR here. I'm sure that's fine. And so it's going to run the CI checks. Let's see what it does. So it's running, check out. Setting Shaz, it's doing NPMCI which is the NPM install, but only the only the packages that are needed for CI. Get branch, just track, origin name. That's fine, it's running target test. It was able to figure out that it needs to run test on API auth and API. It just failed to test, which we expected. It knew that because we only made changes to this particular file here, we only needed to run tests for API auth and an API instead of needing to run tests for all the other projects in the repo as well. So that's the basics of CI here. We could also... We could also do some more complex things. Mayla and Pedro, where do you wanna go from here? Yeah, I mean- Are we digging deeper in CI? Or are we doing other things? Or do you have questions about your repo specifically? No, I'm just processing all that stuff. But yeah, you talked about actions and looks really interesting for my case because I got a lot of components that I want to... kind of when I have to add a new one. I got a doc with all the steps and I think I could do it with a next section which would be really nice. Yeah, I generated it, yeah. And, but yeah, I mean I have to dig in and try to adapt it for my reality but... Yeah, yeah. Mel, I think. Were you the one that had questions at the beginning about, about your specific repo? Yes maybe if you have some advices. Yeah. If, because our own repo was previously a learner one. Yeah, okay. Before, yeah. So it's always packages. So it's not integrated. And I would like to know if it's necessary to migrate it to an integrated one. Well, the only reason you'd want to do that is if you wanted to use executors, if you wanted to use one of our pre-made executors that handles like webpack for you basically or whatever other, let's see, let me look at here. That's really the only thing that you can't do in a package-based repo. As some of the generators in the plugins have an expectation about the folder structure, but that's on a case-by-case basis. But really, the only thing is if you want to use these executors here, then you need to have an integrate, like have a project.json instead of package.json. But that's the only thing you need to do. But if you have no need for that, then you don't need to switch. All the caching will still work. You can still set up a task pipeline and have the builds depend on the... The application build depends on the library builds or other tasks depend on each other. That can all be set up just fine. I can just show you that real quick, actually. If we have a... Let's do nxglib, and make a JS library, and... We'll call it simple. It's fine, it's fine. And let's go here. Simple, and I'm gonna delete the project.json here. And so I... And so I'm gonna just add a scripts here. And build, and say echo... Hello. And so if I run nx build simple, where's it? It ran it, but I think it hid the output. That's not simple. Maybe other verbose performance. Yeah, maybe. I don't know. They haven't been trying that. I go, hello. If I'm trying to find project symbol.
21. Using Workspaces and Analyzing Source Files
We discussed the use of workspaces in a package-based repo and how they allow your package manager to understand that you have other package JSON files inside your repository. By setting the nx property in the npm script, you can add configuration options and specify dependencies between projects using the depends on property. However, by default, the dependency graph is based on the dependencies defined in your package JSON. To use the actual code for dependencies within the repository, you can enable the analyze source files flag in the nxjson file. This will create a dependency graph based on the source files. It's important to note that enabling this flag may reveal unexpected dependencies and circular dependencies. We're almost out of time. Any final questions?
Let's go, I think it might've been cached. Oh, that's why it's called BG horde symptom. Let's do that. There we go, it was cached, it hadn't picked up that I'd switched it. And now I need to reset again. Next build, text json, the name is simple. Oh, that's what it is. If you're using package json, you have to use workspaces in the route. Workspaces, you probably have that already set up. We can do this. Is that the way it works? Is it a, it's an array, isn't it? Is that... Lib slash, something. Those workspace group- There it is. Group the projects, is it? So, this workspaces here is, it's not an index thing. This is a, this is either npm or pnpm or yarn that your package manager has a way of understanding that you have other, basically other package JSON files inside of your repo. And so, when you run npm install, it can run the install for all of them. Okay. So, that's what this is for. So, you can look up, you know, yarn workspaces or npm workspaces. Typically, with a, I think you can do libs star, and that'll just look for everything inside of libs. Yeah, that still works. Okay, anyway. This is running the NPM script. And then in here, say you want to add some configuration to that. You set the, use the nx property here. And then you can say, let's see, targets. So here you can add tags here, like we did before with your scope API or whatever. And then all the other options that you can set in here. So if I go into targets, if I want to set some specific values for the build target, then I can go here and set depends on, and have this depend on here. I'll have it depend on the build base. And echo run this first. So here it ran the build base first and then it ran build because of this depends on property here. Lots of other things you can do with the depends on. You can have it depend on other projects. Or you could say also build the dependencies. Run the build task on dependencies That's what this caret means. The caret means four dependent projects, the dependencies of this project run the build task. So it could be, I don't know, run the test, but that doesn't make a lot of sense. But you can do whatever name you wanted there. Does that make sense, Mayo? Yes. Yes. I think that's what they need. Great. And so it can link the dependencies the same way as integrated one? So it brews the code to know the dependencies? No. Not by default. If you wanted to do that, so by default, in a package-based repo, it goes based on your dependencies that you define in your package JSON because a lot of, so if you have a dependency here on at bghorde API, I think it's, or is it slash? I think it's slash. And then a star. So a dependency on something else that's inside this repo, then it knows that, OK, you've got a dependency here. If you want, instead of using this dependency, if you want the dependency graph to use the actual code, you just need to change in nxjson here. Where is it? Here, I'll look for it in the docs here. Um, what, I always have trouble remembering with this flag. Source. Yes, GraphLinx created from analyzing source files. That's what it is, analyze source files. Here, this is what you want. I'll paste this in the Discord. So this plugins config underneath, underneath the angst.json. So in this particular repo, it's already set to true. It defaults to true. But in a package-based repo, it'll default, this'll default to false. So if you want this to happen for you, then you need to set that to true. OK. And then, so the thing is, when you turn it on to true in a lot of repos, that they'll find dependencies between things that I didn't expect, which makes sense. But then you might have circular dependencies in things that you didn't think were there, but they're actually there. Oh. So I mean, I think it's better to turn it on, but people who just want to get things to work might want to leave it off. All right, I think we're getting close to time. Any other questions? No, thank you, Isaac. All right. Yeah, you're welcome.
22. Trade-offs of Libraries and Applications
It's a trade-off when it comes to having fewer or more libraries and applications. Creating more libraries allows for cacheable targets and the potential for running fewer tests. It also enables setting up constraints between dependencies. However, creating too many libraries can lead to overhead. It's important to have a reasonable number of libraries, with one component per library being too many. When making changes, it's best to have related components in the same library. The same trade-off applies to folder structure, where more folders allow for better organization but can make it harder to find related items.
It's fun to be able to have some time to do individual questions. Maybe just last question. I think I've read that it's better to have less libraries and application as possible. No, it's just a trade-off. So, let me, I'll link this as well. Create library. Hmm. Here we go, this is the article. So, it's just a trade-off here. Weird, cool emoji. So, basically, if you make a new library, then every library you create is another cacheable target. Like if you have everything all in one application, then your cache gets broken with any change you make, right? And your graph is just one node, which is not super useful. But every time you split it up, if you run affected, there's more potential that more of the code will not need to be run, right? More of the tests do not need to be run because they're separated out into libraries that you know aren't affected, right? That's a benefit for making more libraries. And you can set up more constraints, more reasonable constraints around things like, I know these things can depend on each other, but these other things can't. So that's why you wanna have more libraries, but then, negative, there's overhead to making more libraries so you can go overboard and say make thousands of libraries. So you don't wanna do that. But it's just a, there's pros and cons and you just wanna have a reasonable number. And typically, so for me, if you have more, if you have one component per library, that's too many libraries probably. But a handful of components is fine. And basically if you're gonna make a change, make a change to like some feature, you generally want to have the same kinds of things in the same library. If you're gonna make a change, everything you're gonna change should be together in one place. So it's the same thing as with the issues with folder structure. You have the same kind of pros and cons. If more folders, you can separate things out and like really organize them, but then it can be hard to find things that go together. Yeah, okay. Makes sense. Yup. Cool.
Alright, thank you both. And for everyone who's watching online, watching the replay, thanks for sticking out to the end of the video. And yeah. Let me show you, annex.dev, if you have more questions, you know, just go through here and you can go through some of these tutorials or there is also a community Slack here. If you click on this Slack button up here, let me show you what the Slack looks like. Here it is. Support channels are probably the best. So, you know, there's lots of friendly, helpful people. Support is probably the most used channel. If you have some kind of issue that you're running into, lots of friendly and helpful people here. Some, you know, NX employees are in here as well, too. So if you need more help, this is the place to go.
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet? There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more. Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used. You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported. The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials. React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up). Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too. Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step. (Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning? In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests. Table of contents - The different kinds of React application tests, and where component tests fit in - A mental model for thinking about the inputs and outputs of the components you test - Options for selecting DOM elements to verify and interact with them - The value of mocks and why they shouldn’t be avoided - The challenges with asynchrony in RTL tests and how to handle them Prerequisites - Familiarity with building applications with React - Basic experience writing automated tests with Jest or another unit testing framework - You do not need any experience with React Testing Library - Machine setup: Node LTS, Yarn
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock. In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
If you’re building a dashboard, analytics platform, or any web app where you need to give your users insight into their data, you need beautiful, custom, interactive data visualizations in your React app. But building visualizations hand with a low-level library like D3 can be a huge headache, involving lots of wheel-reinventing. In this talk, we’ll see how data viz development can get so much easier thanks to tools like Plot, a high-level dataviz library for quick & easy charting, and Observable, a reactive dataviz prototyping environment, both from the creator of D3. Through live coding examples we’ll explore how React refs let us delegate DOM manipulation for our data visualizations, and how Observable’s embedding functionality lets us easily repurpose community-built visualizations for our own data & use cases. By the end of this talk we’ll know how to get a beautiful, customized, interactive data visualization into our apps with a fraction of the time & effort!
React 18! Concurrent features! You might’ve already tried the new APIs like useTransition, or you might’ve just heard of them. But do you know how React 18 achieves the performance wins it brings with itself? In this talk, let’s peek under the hood of React 18’s performance features: - How React 18 lowers the time your page stays frozen (aka TBT) - What exactly happens in the main thread when you run useTransition() - What’s the catch with the improvements (there’s no free cake!), and why Vue.js and Preact straight refused to ship anything similar